From ff3ab9a97438ea04fa5ea8a0b0e414870db055e9 Mon Sep 17 00:00:00 2001 From: GetParanoid Date: Fri, 18 Jul 2025 15:51:23 -0500 Subject: [PATCH] feat(moar) --- BepInEx/plugins/MOAR.dll | Bin 0 -> 52736 bytes user/mods/DewardianDev-MOAR/LICENSE.md | 21 + .../config/PresetWeightings.json | 19 + .../DewardianDev-MOAR/config/Presets.json | 107 + .../config/Spawns/playerSpawns.json | 262 +++ .../config/Spawns/pmcSpawns.json | 1034 +++++++++ .../config/Spawns/scavSpawns.json | 2049 +++++++++++++++++ .../config/Spawns/sniperSpawns.json | 14 + .../config/advancedConfig.json | 8 + .../DewardianDev-MOAR/config/bossConfig.json | 63 + .../mods/DewardianDev-MOAR/config/config.json | 49 + .../DewardianDev-MOAR/config/mapConfig.json | 185 ++ user/mods/DewardianDev-MOAR/package.json | 25 + .../DewardianDev-MOAR/src/GlobalValues.ts | 17 + .../DewardianDev-MOAR/src/Routes/routes.ts | 213 ++ .../src/SpawnZoneChanges/index.ts | 5 + .../src/SpawnZoneChanges/setupSpawn.ts | 243 ++ .../src/SpawnZoneChanges/updateUtils.ts | 112 + .../src/Spawning/Spawning.ts | 173 ++ .../src/Spawning/buildBossWaves.ts | 341 +++ .../src/Spawning/buildPmcs.ts | 142 ++ .../src/Spawning/buildScavMarksmanWaves.ts | 231 ++ .../src/Spawning/buildZombieWaves.ts | 80 + .../src/Spawning/constants.ts | 232 ++ .../src/Spawning/marksmanChanges.ts | 31 + .../src/Spawning/spawnZoneUtils.ts | 410 ++++ .../src/Spawning/updateSpawnLocations.ts | 71 + .../DewardianDev-MOAR/src/Spawning/utils.ts | 538 +++++ .../src/Tests/checkPresets.ts | 28 + .../DewardianDev-MOAR/src/Zombies/Zombies.ts | 160 ++ user/mods/DewardianDev-MOAR/src/mod.ts | 41 + user/mods/DewardianDev-MOAR/src/utils.ts | 56 + 32 files changed, 6960 insertions(+) create mode 100644 BepInEx/plugins/MOAR.dll create mode 100644 user/mods/DewardianDev-MOAR/LICENSE.md create mode 100644 user/mods/DewardianDev-MOAR/config/PresetWeightings.json create mode 100644 user/mods/DewardianDev-MOAR/config/Presets.json create mode 100644 user/mods/DewardianDev-MOAR/config/Spawns/playerSpawns.json create mode 100644 user/mods/DewardianDev-MOAR/config/Spawns/pmcSpawns.json create mode 100644 user/mods/DewardianDev-MOAR/config/Spawns/scavSpawns.json create mode 100644 user/mods/DewardianDev-MOAR/config/Spawns/sniperSpawns.json create mode 100644 user/mods/DewardianDev-MOAR/config/advancedConfig.json create mode 100644 user/mods/DewardianDev-MOAR/config/bossConfig.json create mode 100644 user/mods/DewardianDev-MOAR/config/config.json create mode 100644 user/mods/DewardianDev-MOAR/config/mapConfig.json create mode 100644 user/mods/DewardianDev-MOAR/package.json create mode 100644 user/mods/DewardianDev-MOAR/src/GlobalValues.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Routes/routes.ts create mode 100644 user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/index.ts create mode 100644 user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/setupSpawn.ts create mode 100644 user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/updateUtils.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/constants.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/marksmanChanges.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/spawnZoneUtils.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Spawning/utils.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts create mode 100644 user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts create mode 100644 user/mods/DewardianDev-MOAR/src/mod.ts create mode 100644 user/mods/DewardianDev-MOAR/src/utils.ts diff --git a/BepInEx/plugins/MOAR.dll b/BepInEx/plugins/MOAR.dll new file mode 100644 index 0000000000000000000000000000000000000000..6d8603e3480650d44917a608bb5a1c390e1da385 GIT binary patch literal 52736 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 bVV0hgYs^% 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 XpJ%Yrp$pYZ|2OZ)|1bVurGftoohkX7 literal 0 HcmV?d00001 diff --git a/user/mods/DewardianDev-MOAR/LICENSE.md b/user/mods/DewardianDev-MOAR/LICENSE.md new file mode 100644 index 0000000..35fbeeb --- /dev/null +++ b/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/user/mods/DewardianDev-MOAR/config/PresetWeightings.json b/user/mods/DewardianDev-MOAR/config/PresetWeightings.json new file mode 100644 index 0000000..d6cbca4 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/PresetWeightings.json @@ -0,0 +1,19 @@ +{ + "live-like": 45, + "quiet-raids": 8, + "more-scavs": 5, + "more-pmcs": 4, + "more-scavs-and-pmcs": 3, + "delayed-scavs": 2, + "delayed-pmcs": 4, + "arrived-early": 4, + "rogue-invasion": 1, + "raider-invasion": 1, + "insanity": 1, + "minor-boss-invasion": 4, + "less-pmcs-less-scavs-boss-invasion": 3, + "low-pmcs-low-scavs-major-boss-invasion": 1, + "main-boss-guaranteed": 5, + "main-boss-guaranteed-roaming": 3, + "sniper-buddies": 5 +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/Presets.json b/user/mods/DewardianDev-MOAR/config/Presets.json new file mode 100644 index 0000000..13c3dd5 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/Presets.json @@ -0,0 +1,107 @@ +{ + "live-like": {}, + "quiet-raids": { + "spawnSmoothing": false, + "randomSpawns": true, + "scavGroupChance": 0.1, + "scavMaxGroupSize": 2, + "pmcGroupChance": 0.1, + "pmcMaxGroupSize": 2, + "scavWaveQuantity": 0.7, + "pmcWaveQuantity": 0.5, + "mainBossChanceBuff": 0 + }, + "more-scavs": { + "scavGroupChance": 0.5, + "scavMaxGroupSize": 5, + "scavWaveQuantity": 1.5, + "pmcWaveQuantity": 0.7 + }, + "more-pmcs": { + "pmcGroupChance": 0.5, + "pmcWaveQuantity": 1.3, + "scavWaveQuantity": 0.7 + }, + "more-scavs-and-pmcs": { + "scavGroupChance": 0.3, + "scavMaxGroupSize": 5, + "pmcGroupChance": 0.3, + "pmcMaxGroupSize": 5, + "scavWaveQuantity": 1.4, + "pmcWaveQuantity": 1.2 + }, + "delayed-scavs": { + "scavWaveDistribution": 1.5 + }, + "delayed-pmcs": { + "pmcWaveDistribution": 1.2 + }, + "arrived-early": { + "spawnSmoothing": false, + "scavWaveDistribution": 1.2, + "pmcWaveDistribution": 1.2 + }, + "rogue-invasion": { + "randomRaiderGroup": true, + "randomRaiderGroupChance": 100 + }, + "raider-invasion": { + "randomRaiderGroup": true, + "randomRaiderGroupChance": 100 + }, + "insanity": { + "scavWaveQuantity": 1.3, + "pmcWaveQuantity": 1.3, + "scavGroupChance": 0.7, + "pmcGroupChance": 0.8, + "pmcMaxGroupSize": 6, + "scavMaxGroupSize": 6, + "sniperGroupChance": 0.5, + "bossOpenZones": true, + "randomRaiderGroup": true, + "randomRaiderGroupChance": 50, + "randomRogueGroup": true, + "randomRogueGroupChance": 50, + "mainBossChanceBuff": 50, + "bossInvasion": true, + "bossInvasionSpawnChance": 10 + }, + "minor-boss-invasion": { + "bossOpenZones": true, + "bossInvasion": true, + "bossInvasionSpawnChance": 10, + "mainBossChanceBuff": 25, + "gradualBossInvasion": true + }, + "less-pmcs-less-scavs-boss-invasion": { + "maxBotCap": 20, + "scavWaveQuantity": 0.7, + "pmcWaveQuantity": 0.7, + "bossOpenZones": true, + "bossInvasion": true, + "bossInvasionSpawnChance": 25, + "mainBossChanceBuff": 35, + "gradualBossInvasion": true + }, + "low-pmcs-low-scavs-major-boss-invasion": { + "maxBotCap": 12, + "scavWaveQuantity": 0.3, + "pmcWaveQuantity": 0.1, + "bossOpenZones": true, + "bossInvasion": true, + "bossInvasionSpawnChance": 90, + "mainBossChanceBuff": 80, + "gradualBossInvasion": true + }, + "main-boss-guaranteed": { + "mainBossChanceBuff": 100 + }, + "main-boss-guaranteed-roaming": { + "bossOpenZones": true, + "mainBossChanceBuff": 100 + }, + "sniper-buddies": { + "sniperMaxGroupSize": 4, + "sniperGroupChance": 1 + } +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/Spawns/playerSpawns.json b/user/mods/DewardianDev-MOAR/config/Spawns/playerSpawns.json new file mode 100644 index 0000000..5a56357 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/Spawns/playerSpawns.json @@ -0,0 +1,262 @@ +{ + "bigmap": [ + { + "x": -237.231491, + "y": 1.75956392, + "z": -120.84491 + }, + { + "x": -333.538757, + "y": 1.2834585, + "z": -193.426743 + }, + { + "x": -330.967346, + "y": 0.7311232, + "z": -148.138031 + }, + { + "x": -320.449768, + "y": -0.1416501, + "z": -67.51158 + }, + { + "x": 494.7378, + "y": 2.0099400000000003, + "z": 214.363235 + }, + { + "x": 406.431763, + "y": 13.6794415, + "z": 205.624054 + }, + { + "x": 565.3794, + "y": 13.6622076, + "z": 176.73848 + }, + { + "x": 670.381958, + "y": 4.553419, + "z": 116.672173 + }, + { + "x": 648.4502, + "y": 0.884521127, + "z": 110.214828 + }, + { + "x": 648.0046, + "y": 1.60148263, + "z": -144.927017 + }, + { + "x": 607.7813, + "y": 1.9404974, + "z": -131.76033 + }, + { + "x": 353.7437, + "y": 1.67306828, + "z": -182.096039 + } + ], + "factory4_day": [], + "factory4_night": [], + "interchange": [ + { + "x": 281.41922, + "y": 21.82544, + "z": 350.742 + }, + { + "x": 485.793152, + "y": 19.02228, + "z": -376.709045 + }, + { + "x": -244.620956, + "y": 21.9201584, + "z": -249.0072 + } + ], + "laboratory": [], + "lighthouse": [ + { + "x": -6.39022541, + "y": 0.612077177, + "z": 582.3435 + }, + { + "x": 151.168228, + "y": 1.59125161, + "z": 332.439056 + }, + { + "x": 146.495163, + "y": 0.8794734479999999, + "z": -160.836212 + } + ], + "rezervbase": [ + { + "x": 233.31601, + "y": -10.2675419, + "z": -4.18630457 + }, + { + "x": 139.089874, + "y": -10.305027, + "z": 23.3623848 + }, + { + "x": 56.89732, + "y": -6.44232559, + "z": 137.955032 + } + ], + "shoreline": [ + { + "x": 257.83, + "y": -53.64759, + "z": -104.635 + }, + { + "x": 155.2863, + "y": -33.8709831, + "z": -282.204285 + }, + { + "x": 249.29483, + "y": -64.13083, + "z": 450.0022 + }, + { + "x": -529.4264, + "y": -18.0687866, + "z": -338.916351 + } + ], + "tarkovstreets": [], + "woods": [ + { + "x": -255.085876, + "y": 18.7673836, + "z": -657.3163 + }, + { + "x": 216.3761, + "y": 11.7787333, + "z": -831.962036 + }, + { + "x": -629.5677, + "y": 13.3888264, + "z": -257.016571 + }, + { + "x": -602.1799, + "y": 24.6817856, + "z": -154.038223 + }, + { + "x": 253.6307, + "y": -10.6299782, + "z": 331.7935 + } + ], + "sandbox": [ + { + "x": 12.19389, + "y": 23.9751911, + "z": 157.1094 + }, + { + "x": 109.844994, + "y": 23.96955, + "z": 304.835266 + }, + { + "x": 139.231842, + "y": 23.92805, + "z": 275.822052 + }, + { + "x": 120.96067, + "y": 28.6914253, + "z": 285.82135 + }, + { + "x": 147.699371, + "y": 23.2307129, + "z": 255.266312 + }, + { + "x": 5.82757664, + "y": 23.2639179, + "z": 330.428345 + }, + { + "x": -11.9459629, + "y": 24.33598, + "z": -57.9175034 + }, + { + "x": 207.106964, + "y": 16.67053, + "z": 37.7381 + }, + { + "x": 181.100342, + "y": 16.74511, + "z": 125.971664 + } + ], + "sandbox_high": [ + { + "x": 12.19389, + "y": 23.9751911, + "z": 157.1094 + }, + { + "x": 109.844994, + "y": 23.96955, + "z": 304.835266 + }, + { + "x": 139.231842, + "y": 23.92805, + "z": 275.822052 + }, + { + "x": 120.96067, + "y": 28.6914253, + "z": 285.82135 + }, + { + "x": 147.699371, + "y": 23.2307129, + "z": 255.266312 + }, + { + "x": 5.82757664, + "y": 23.2639179, + "z": 330.428345 + }, + { + "x": -11.9459629, + "y": 24.33598, + "z": -57.9175034 + }, + { + "x": 207.106964, + "y": 16.67053, + "z": 37.7381 + }, + { + "x": 181.100342, + "y": 16.74511, + "z": 125.971664 + } + ] +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/Spawns/pmcSpawns.json b/user/mods/DewardianDev-MOAR/config/Spawns/pmcSpawns.json new file mode 100644 index 0000000..5c09b24 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/Spawns/pmcSpawns.json @@ -0,0 +1,1034 @@ +{ + "bigmap": [ + { + "x": -235.514816, + "y": 1.71511328, + "z": -90.65792 + }, + { + "x": 375.373779, + "y": 11.0117579, + "z": 155.3459 + }, + { + "x": 539.9962, + "y": 12.5253057, + "z": 165.450333 + }, + { + "x": 586.187561, + "y": 9.813362, + "z": 141.6987 + }, + { + "x": 617.64, + "y": 2.97147727, + "z": 87.2145538 + }, + { + "x": 649.393738, + "y": 2.4090439999999997, + "z": 49.1529655 + }, + { + "x": 647.832031, + "y": 0.45541042460000003, + "z": -16.4535866 + }, + { + "x": 570.888, + "y": 9.679792, + "z": -124.946266 + }, + { + "x": 573.312439, + "y": 7.23252, + "z": -55.1983948 + }, + { + "x": 385.6326, + "y": 1.84112751, + "z": -109.0065 + }, + { + "x": 327.154022, + "y": 10.006629, + "z": -74.3271561 + }, + { + "x": 346.438171, + "y": 1.72811913, + "z": -77.25114 + }, + { + "x": 314.941437, + "y": 1.66246521, + "z": -90.0657654 + }, + { + "x": 285.944763, + "y": 1.96794569, + "z": -40.2178154 + }, + { + "x": 275.077545, + "y": 2.044823, + "z": -13.4269781 + }, + { + "x": 249.480133, + "y": 5.72205544, + "z": -33.0787659 + }, + { + "x": 261.793854, + "y": 6.36171961, + "z": -66.88628 + }, + { + "x": 292.700531, + "y": 1.606755, + "z": -82.14554 + }, + { + "x": 249.8792, + "y": 1.82383776, + "z": -216.285156 + }, + { + "x": 86.2089844, + "y": 1.62022269, + "z": -169.991531 + }, + { + "x": -18.3804474, + "y": -7.07633972, + "z": 100.548088 + }, + { + "x": 488.2746, + "y": 4.633864, + "z": -147.154587 + }, + { + "x": 274.49176, + "y": 3.42755079, + "z": 151.438309 + }, + { + "x": 180.905624, + "y": 1.7817235, + "z": -226.564331 + }, + { + "x": 82.4062958, + "y": 1.98480439, + "z": -146.307114 + }, + { + "x": -215.166428, + "y": 1.68568707, + "z": -106.748978 + }, + { + "x": -228.426392, + "y": 1.301838636, + "z": -150.367218 + }, + { + "x": 184.013443, + "y": -1.26495123, + "z": 201.694061 + }, + { + "x": 294.7861, + "y": 4.07159734, + "z": 96.28285 + }, + { + "x": 320.965637, + "y": 4.698986, + "z": 11.9264107 + }, + { + "x": 270.553558, + "y": 4.635361, + "z": 181.503891 + }, + { + "x": 255.152527, + "y": 0.43899893759999997, + "z": 160.279846 + }, + { + "x": 247.165329, + "y": -0.11888974900000004, + "z": 134.349274 + }, + { + "x": 224.211563, + "y": -0.20601539999999996, + "z": 114.647507 + }, + { + "x": 191.076233, + "y": -0.9109389999999999, + "z": 110.596725 + }, + { + "x": 151.840515, + "y": -0.6054667199999999, + "z": 121.658409 + }, + { + "x": 125.400879, + "y": 0.4505230263, + "z": 133.79393 + }, + { + "x": 32.0457039, + "y": -0.16172419999999998, + "z": 121.800728 + }, + { + "x": -117.757523, + "y": -4.037934, + "z": 36.26771 + }, + { + "x": -125.734436, + "y": -3.00606179, + "z": -26.928791 + } + ], + "factory4_day": [ + { + "x": -5.679021, + "y": 8.881359, + "z": -26.3386173 + }, + { + "x": 9.079021, + "y": 4.0276048200000005, + "z": -38.5319939 + }, + { + "x": 28.2991257, + "y": 4.07116866, + "z": -31.3675442 + }, + { + "x": 68.7076645, + "y": 0.5511049777, + "z": -7.81235647 + }, + { + "x": 11.2736158, + "y": 1.64256132, + "z": 58.3516159 + }, + { + "x": 29.4325447, + "y": 1.4928648469999999, + "z": 43.576622 + } + ], + "factory4_night": [ + { + "x": -5.679021, + "y": 8.881359, + "z": -26.3386173 + }, + { + "x": 9.079021, + "y": 4.0276048200000005, + "z": -38.5319939 + }, + { + "x": 28.2991257, + "y": 4.07116866, + "z": -31.3675442 + }, + { + "x": 68.7076645, + "y": 0.5511049777, + "z": -7.81235647 + }, + { + "x": 11.2736158, + "y": 1.64256132, + "z": 58.3516159 + }, + { + "x": 29.4325447, + "y": 1.4928648469999999, + "z": 43.576622 + } + ], + "interchange": [ + { + "x": 446.206116, + "y": 18.1065464, + "z": -292.665222 + }, + { + "x": 124.291664, + "y": 21.826746, + "z": 271.128418 + }, + { + "x": -344.8051, + "y": 21.50156, + "z": 264.128967 + }, + { + "x": -326.361664, + "y": 21.8861561, + "z": 261.583374 + }, + { + "x": -287.247681, + "y": 21.8254337, + "z": 225.601791 + }, + { + "x": -67.20804, + "y": 21.8254356, + "z": -353.0317 + }, + { + "x": 13.7416363, + "y": 21.8933, + "z": -255.47084 + }, + { + "x": -45.6252861, + "y": 21.9879189, + "z": -64.93814 + }, + { + "x": -124.404755, + "y": 27.5862, + "z": -166.61203 + }, + { + "x": -60.7056847, + "y": 27.6426926, + "z": 60.014164 + }, + { + "x": -13.0514126, + "y": 27.5936089, + "z": 53.85792 + }, + { + "x": -59.0381966, + "y": 27.58648, + "z": 85.46995 + }, + { + "x": 57.26041, + "y": 27.5913048, + "z": -4.55139875 + }, + { + "x": 76.51547, + "y": 27.58646, + "z": 62.9798431 + }, + { + "x": -3.152739, + "y": 27.5864754, + "z": -54.5857468 + }, + { + "x": -19.0746117, + "y": 27.6992855, + "z": -43.3713074 + }, + { + "x": -2.914417, + "y": 27.6992855, + "z": -25.9387226 + }, + { + "x": 8.257145, + "y": 27.6491566, + "z": -12.7488337 + }, + { + "x": -132.975723, + "y": 27.6298332, + "z": 13.0758 + }, + { + "x": 103.686417, + "y": 21.9332, + "z": -58.8592529 + }, + { + "x": 38.5032845, + "y": 37.0696449, + "z": -64.47136 + }, + { + "x": 72.4890442, + "y": 37.1878052, + "z": -64.1576157 + }, + { + "x": 95.6903839, + "y": 37.1878052, + "z": -53.1560249 + }, + { + "x": -4.06011152, + "y": 37.06964, + "z": -29.1265564 + }, + { + "x": -32.19452, + "y": 37.06964, + "z": -44.561512 + }, + { + "x": -26.38217, + "y": 21.9310627, + "z": 22.2457352 + }, + { + "x": 29.7248058, + "y": 21.83612, + "z": 33.8304558 + }, + { + "x": 31.37312, + "y": 21.8254242, + "z": 151.695831 + } + ], + "laboratory": [], + "lighthouse": [ + { + "x": -138.759735, + "y": 22.344717, + "z": 298.995026 + }, + { + "x": 107.747215, + "y": 0.646862835, + "z": 551.5041 + }, + { + "x": 119.693924, + "y": 0.815569371, + "z": -163.307343 + }, + { + "x": -407.4164, + "y": 14.8516836, + "z": -475.0829 + }, + { + "x": -124.275848, + "y": 1.084059656, + "z": -353.473755 + }, + { + "x": -344.0873, + "y": 10.78395, + "z": -189.107147 + }, + { + "x": -72.95316, + "y": 13.4610224, + "z": 237.904449 + }, + { + "x": -52.01755, + "y": 9.415716, + "z": 280.982971 + }, + { + "x": -126.961792, + "y": 0.624748014, + "z": 525.7007 + }, + { + "x": -273.731537, + "y": 14.330102, + "z": -195.1435 + }, + { + "x": -262.871979, + "y": 1.4993639, + "z": -348.9483 + }, + { + "x": -30.5226479, + "y": 12.4621935, + "z": -886.108643 + }, + { + "x": 55.59912, + "y": 11.0115089, + "z": -896.057251 + }, + { + "x": 107.381256, + "y": 5.11968136, + "z": -934.385559 + }, + { + "x": -344.3486, + "y": 27.353611, + "z": -658.0147 + }, + { + "x": -125.909645, + "y": 32.9849548, + "z": 162.170563 + } + ], + "rezervbase": [ + { + "x": -0.8466827, + "y": -6.56154871, + "z": -200.959717 + }, + { + "x": 69.502594, + "y": -6.48131371, + "z": -225.894562 + }, + { + "x": -292.510254, + "y": -5.76621151, + "z": -78.32959 + }, + { + "x": -259.83963, + "y": -5.44681168, + "z": 51.8360252 + }, + { + "x": -32.25938, + "y": 19.0377769, + "z": 173.245392 + }, + { + "x": -66.12207, + "y": -15.323307, + "z": 166.748627 + }, + { + "x": -115.467636, + "y": -14.0357494, + "z": 37.0463066 + }, + { + "x": 272.105133, + "y": -6.473955, + "z": 13.4919682 + }, + { + "x": 218.881165, + "y": -6.451986, + "z": -110.935707 + }, + { + "x": 189.465942, + "y": -6.30287933, + "z": -216.501923 + }, + { + "x": 162.844543, + "y": -6.343725, + "z": -236.902481 + }, + { + "x": -7.877653, + "y": 19.5167141, + "z": 191.588013 + } + ], + "shoreline": [ + { + "x": -292.390167, + "y": -34.5392532, + "z": 157.488846 + }, + { + "x": -110.234634, + "y": -41.6665573, + "z": 355.3756 + }, + { + "x": 33.320118, + "y": -46.29929, + "z": 119.379257 + }, + { + "x": -414.384949, + "y": -18.00421, + "z": -269.216827 + }, + { + "x": -108.520767, + "y": -41.01313, + "z": 274.7408 + }, + { + "x": -713.2729, + "y": -46.5427856, + "z": 376.357727 + }, + { + "x": -810.128845, + "y": -52.685215, + "z": 357.9311 + }, + { + "x": -831.979, + "y": -44.0417328, + "z": 104.6881 + }, + { + "x": 48.83184, + "y": -36.6432571, + "z": -17.975769 + }, + { + "x": 38.7559357, + "y": -20.93227, + "z": -113.655991 + }, + { + "x": -663.9199, + "y": -26.33351, + "z": -241.42218 + }, + { + "x": -684.342163, + "y": -26.0761948, + "z": -215.379089 + }, + { + "x": -674.3177, + "y": -26.3148174, + "z": -181.81842 + }, + { + "x": -527.282043, + "y": -18.1195164, + "z": -334.8053 + }, + { + "x": -7.023548, + "y": -59.17309, + "z": 373.640076 + }, + { + "x": -389.126465, + "y": -59.2554932, + "z": 450.100037 + }, + { + "x": -475.738678, + "y": -59.255497, + "z": 455.802338 + }, + { + "x": -645.7365, + "y": -53.13785, + "z": 346.057831 + }, + { + "x": -726.2845, + "y": -41.13844, + "z": 334.0819 + }, + { + "x": -545.7296, + "y": -31.0189171, + "z": -128.043472 + } + ], + "tarkovstreets": [], + "woods": [ + { + "x": 369.19278, + "y": 0.53812469, + "z": -163.258347 + }, + { + "x": 293.084, + "y": 7.1461, + "z": -251.827377 + }, + { + "x": 33.17424, + "y": 35.81081, + "z": -327.0943 + }, + { + "x": 24.5087166, + "y": 23.7123184, + "z": -396.8567 + }, + { + "x": -160.655685, + "y": 19.0995445, + "z": -424.976471 + }, + { + "x": 66.99521, + "y": 15.74468, + "z": -790.8915 + }, + { + "x": 304.1156, + "y": 23.581913, + "z": -611.909241 + }, + { + "x": 21.492918, + "y": 11.8092976, + "z": -550.3476 + }, + { + "x": -511.078552, + "y": 14.204133, + "z": -91.32903 + }, + { + "x": -527.2279, + "y": 7.76844358, + "z": 148.111267 + }, + { + "x": -178.88089, + "y": 50.893055, + "z": -222.052734 + }, + { + "x": -535.668152, + "y": 11.0035973, + "z": -133.141312 + }, + { + "x": 194.299759, + "y": -14.4182987, + "z": 262.024658 + }, + { + "x": 289.369263, + "y": 12.0692568, + "z": -566.685852 + }, + { + "x": -46.5257339, + "y": 9.384923, + "z": -602.5932 + }, + { + "x": -80.87197, + "y": -4.05404329, + "z": 158.338455 + }, + { + "x": -286.2295, + "y": 11.0952225, + "z": -44.5139275 + }, + { + "x": -227.722748, + "y": 11.0703936, + "z": -18.3279839 + }, + { + "x": -164.433945, + "y": 12.0377178, + "z": 18.6597538 + }, + { + "x": -20.99698, + "y": 15.6939211, + "z": -231.1298 + }, + { + "x": -48.5224762, + "y": 21.8124084, + "z": -261.8402 + }, + { + "x": -0.223655358, + "y": 25.13237, + "z": -277.855 + }, + { + "x": -79.7386856, + "y": 28.3564415, + "z": -324.288452 + }, + { + "x": -589.8979, + "y": 14.0588245, + "z": -370.177032 + }, + { + "x": -407.0367, + "y": 16.98802, + "z": -345.946869 + }, + { + "x": -339.451752, + "y": 9.988096, + "z": -558.315552 + }, + { + "x": -253.624252, + "y": 11.9352074, + "z": -637.524841 + }, + { + "x": 5.48920965, + "y": 10.7130795, + "z": -744.885254 + }, + { + "x": 109.74057, + "y": 21.4577579, + "z": -396.137115 + }, + { + "x": 233.20343, + "y": 27.7301483, + "z": -320.3957 + }, + { + "x": 188.078613, + "y": 28.4282646, + "z": -309.707458 + }, + { + "x": 114.109009, + "y": 26.26976, + "z": -324.133362 + }, + { + "x": 67.88875, + "y": 32.3777447, + "z": -325.8311 + }, + { + "x": 356.3839, + "y": 15.6048393, + "z": -375.216034 + }, + { + "x": 353.8394, + "y": 11.2490816, + "z": -441.2975 + }, + { + "x": 246.337921, + "y": 4.02692771, + "z": -231.689377 + }, + { + "x": 293.684967, + "y": -2.987185, + "z": 80.18842 + }, + { + "x": 265.2839, + "y": -9.178231, + "z": 189.151733 + }, + { + "x": 49.1535759, + "y": -7.177124, + "z": 64.50428 + }, + { + "x": 10.7948055, + "y": -3.177351, + "z": 42.5892677 + }, + { + "x": 55.7691536, + "y": -2.17705059, + "z": -79.7449341 + }, + { + "x": -96.46032, + "y": 7.068636, + "z": 60.8101654 + } + ], + "sandbox": [ + { + "x": 129.421143, + "y": 23.9573441, + "z": 293.009033 + }, + { + "x": 113.164185, + "y": 33.319603, + "z": 282.538727 + }, + { + "x": 48.6474152, + "y": 23.9374447, + "z": 157.17392 + }, + { + "x": 115.092773, + "y": 24.9380016, + "z": 142.257156 + }, + { + "x": 101.9017, + "y": 29.4764881, + "z": 111.925575 + }, + { + "x": 128.609482, + "y": 24.9139576, + "z": 92.77304 + }, + { + "x": -26.6534958, + "y": 24.7057018, + "z": 22.3732262 + }, + { + "x": -26.66907, + "y": 24.6810627, + "z": -16.0451965 + }, + { + "x": -41.6530876, + "y": 30.2298813, + "z": 63.5666428 + }, + { + "x": -15.5884218, + "y": 30.2231712, + "z": 76.8380051 + }, + { + "x": 175.026276, + "y": 17.428, + "z": 28.9653358 + }, + { + "x": 50.16706, + "y": 31.40412, + "z": 147.336731 + }, + { + "x": 78.1081543, + "y": 14.5159016, + "z": 173.353226 + }, + { + "x": 130.68251, + "y": 23.3459148, + "z": 7.57552528 + }, + { + "x": 107.514496, + "y": 24.9085712, + "z": 93.20195 + }, + { + "x": 216.797638, + "y": 16.517622, + "z": 33.8977356 + } + ], + "sandbox_high": [ + { + "x": 129.421143, + "y": 23.9573441, + "z": 293.009033 + }, + { + "x": 113.164185, + "y": 33.319603, + "z": 282.538727 + }, + { + "x": 48.6474152, + "y": 23.9374447, + "z": 157.17392 + }, + { + "x": 115.092773, + "y": 24.9380016, + "z": 142.257156 + }, + { + "x": 101.9017, + "y": 29.4764881, + "z": 111.925575 + }, + { + "x": 128.609482, + "y": 24.9139576, + "z": 92.77304 + }, + { + "x": -26.6534958, + "y": 24.7057018, + "z": 22.3732262 + }, + { + "x": -26.66907, + "y": 24.6810627, + "z": -16.0451965 + }, + { + "x": -41.6530876, + "y": 30.2298813, + "z": 63.5666428 + }, + { + "x": -15.5884218, + "y": 30.2231712, + "z": 76.8380051 + }, + { + "x": 175.026276, + "y": 17.428, + "z": 28.9653358 + }, + { + "x": 50.16706, + "y": 31.40412, + "z": 147.336731 + }, + { + "x": 78.1081543, + "y": 14.5159016, + "z": 173.353226 + }, + { + "x": 130.68251, + "y": 23.3459148, + "z": 7.57552528 + }, + { + "x": 107.514496, + "y": 24.9085712, + "z": 93.20195 + }, + { + "x": 216.797638, + "y": 16.517622, + "z": 33.8977356 + } + ] +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/Spawns/scavSpawns.json b/user/mods/DewardianDev-MOAR/config/Spawns/scavSpawns.json new file mode 100644 index 0000000..41bfdc5 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/Spawns/scavSpawns.json @@ -0,0 +1,2049 @@ +{ + "bigmap": [ + { + "x": -284.943146, + "y": 1.4239985, + "z": -136.588181 + }, + { + "x": -304.006348, + "y": 1.4297495, + "z": -179.318909 + }, + { + "x": -208.90892, + "y": 1.77403331, + "z": -225.2736 + }, + { + "x": -237.128616, + "y": 1.72657418, + "z": -121.247658 + }, + { + "x": -234.247025, + "y": 1.93498886, + "z": -151.727432 + }, + { + "x": 457.799622, + "y": 6.07589054, + "z": 126.373367 + }, + { + "x": 497.4013, + "y": 13.7815294, + "z": 133.515411 + }, + { + "x": 513.3094, + "y": 12.3549109, + "z": 155.173431 + }, + { + "x": 573.964539, + "y": 10.860033, + "z": 109.094734 + }, + { + "x": 599.232056, + "y": 6.82307, + "z": 101.392273 + }, + { + "x": 654.828369, + "y": 1.66011035, + "z": 77.30017 + }, + { + "x": 624.695251, + "y": -0.4742429, + "z": 30.6330967 + }, + { + "x": 643.2286, + "y": 5.5803256, + "z": 12.3052654 + }, + { + "x": 650.086548, + "y": 0.108204782, + "z": -33.0176468 + }, + { + "x": 605.179565, + "y": 0.5171957985, + "z": -12.2007494 + }, + { + "x": 618.131348, + "y": -0.102399766, + "z": -86.60424 + }, + { + "x": 540.1409, + "y": 1.91840613, + "z": -124.3964 + }, + { + "x": 565.567566, + "y": 4.530571, + "z": -125.521751 + }, + { + "x": 566.3823, + "y": 7.17525959, + "z": -49.90116 + }, + { + "x": 562.5062, + "y": 1.92474866, + "z": -81.12562 + }, + { + "x": 367.363861, + "y": 1.84112751, + "z": -99.93791 + }, + { + "x": 317.8741, + "y": 1.57443154, + "z": -71.61143 + }, + { + "x": 296.8635, + "y": 1.63690567, + "z": -35.6471 + }, + { + "x": 275.538147, + "y": 6.9441967, + "z": -33.89297 + }, + { + "x": 260.338928, + "y": 2.03420162, + "z": -66.23898 + }, + { + "x": 190.591125, + "y": 8.267894, + "z": -103.392845 + }, + { + "x": 213.910629, + "y": 8.26133871, + "z": -140.68988 + }, + { + "x": 223.9906, + "y": 1.64600563, + "z": -244.305008 + }, + { + "x": 183.847977, + "y": 1.58706868, + "z": -214.653824 + }, + { + "x": 59.850563, + "y": 1.59559584, + "z": -170.731049 + }, + { + "x": -59.9128723, + "y": -6.22277355, + "z": -160.477219 + }, + { + "x": -102.996346, + "y": -9.146751, + "z": -39.917366 + }, + { + "x": 134.103119, + "y": 0.5236490928, + "z": 72.98761 + }, + { + "x": 109.656487, + "y": 1.84072864, + "z": -94.5954 + }, + { + "x": 510.716064, + "y": 6.323722, + "z": -65.52496 + }, + { + "x": 509.49527, + "y": 1.5242126, + "z": 28.2894745 + }, + { + "x": 334.346222, + "y": 6.820747, + "z": 120.493515 + }, + { + "x": 559.193054, + "y": 4.6338954, + "z": -150.453537 + }, + { + "x": 519.3548, + "y": 4.58627224, + "z": -145.119415 + }, + { + "x": 435.42157, + "y": 4.595445, + "z": -135.167938 + }, + { + "x": 258.943359, + "y": 2.73439646, + "z": -196.011139 + }, + { + "x": 79.89013, + "y": 5.172777, + "z": -161.127045 + }, + { + "x": -200.951248, + "y": 5.22553158, + "z": -78.152626 + }, + { + "x": -164.034973, + "y": 3.62881374, + "z": -143.72673 + }, + { + "x": -152.506119, + "y": 1.80959785, + "z": -23.5344086 + }, + { + "x": 138.431686, + "y": -1.39192617, + "z": 149.869308 + }, + { + "x": 142.960464, + "y": -1.4955076, + "z": 199.643127 + }, + { + "x": 370.805939, + "y": 12.5912714, + "z": 166.302643 + }, + { + "x": 384.022247, + "y": 9.518441, + "z": 150.392975 + }, + { + "x": 399.403229, + "y": 12.4634857, + "z": 192.232727 + }, + { + "x": 421.795532, + "y": 8.753826, + "z": 194.022308 + }, + { + "x": 484.7756, + "y": 2.4937655899999998, + "z": 181.999313 + }, + { + "x": 552.7636, + "y": 14.1131525, + "z": 154.6426 + }, + { + "x": 611.4055, + "y": 6.81429625, + "z": 121.44825 + }, + { + "x": 622.02594, + "y": -1.24542069, + "z": 64.11434 + }, + { + "x": 423.989471, + "y": 1.3427196000000001, + "z": -3.61689377 + }, + { + "x": 366.947418, + "y": 1.102021158, + "z": 4.726229 + }, + { + "x": 342.511871, + "y": 1.59022391, + "z": -19.0248528 + }, + { + "x": 316.0861, + "y": 4.67861271, + "z": -14.7028484 + }, + { + "x": 340.592377, + "y": 4.56583929, + "z": 64.40153 + }, + { + "x": 380.688263, + "y": 4.53000355, + "z": 95.3672256 + }, + { + "x": 297.7464, + "y": 6.81623173, + "z": 170.3272 + }, + { + "x": 179.791748, + "y": -1.34051752, + "z": 78.20171 + }, + { + "x": 202.187271, + "y": -0.5058654499999999, + "z": 146.521149 + }, + { + "x": 210.978409, + "y": -0.214001954, + "z": 167.01918 + }, + { + "x": 244.4779, + "y": -0.21140579999999998, + "z": 180.034119 + }, + { + "x": 248.335678, + "y": -0.18326884499999996, + "z": 149.3474 + }, + { + "x": 233.77446, + "y": -0.17877864799999998, + "z": 121.57341 + }, + { + "x": 209.262527, + "y": -0.23224129999999998, + "z": 118.995377 + }, + { + "x": 157.5238, + "y": -0.48610830000000005, + "z": 112.498535 + }, + { + "x": 142.01239, + "y": -0.86573565, + "z": 125.039192 + }, + { + "x": 95.45312, + "y": -2.82561374, + "z": 154.969 + }, + { + "x": 60.5261574, + "y": -3.904211, + "z": 121.961784 + }, + { + "x": 77.03402, + "y": -2.85970068, + "z": 85.12606 + } + ], + "factory4_day": [ + { + "x": -31.5008049, + "y": -2.09620523, + "z": 32.2005348 + }, + { + "x": -18.7847786, + "y": -2.097631, + "z": 40.29317 + }, + { + "x": 16.9372578, + "y": -2.08116531, + "z": 40.84489 + }, + { + "x": 23.5751019, + "y": -2.101208, + "z": 7.9283185 + }, + { + "x": 19.4345531, + "y": -2.101208, + "z": -13.3914032 + }, + { + "x": 27.6461773, + "y": -2.101208, + "z": -34.54554 + }, + { + "x": 38.79806, + "y": -2.09461427, + "z": -18.6310368 + }, + { + "x": 31.9105778, + "y": 7.618354, + "z": -32.1087 + }, + { + "x": 30.08668, + "y": 7.75526237, + "z": -20.3167381 + }, + { + "x": 21.4689655, + "y": 8.888684, + "z": 7.54276848 + }, + { + "x": 6.222929, + "y": 7.68675137, + "z": 16.6077843 + }, + { + "x": -22.1931133, + "y": 7.98713732, + "z": 11.6042461 + }, + { + "x": 1.68692517, + "y": 8.942114, + "z": -11.3421993 + }, + { + "x": -1.84703875, + "y": 0.14306390000000002, + "z": -47.23748 + }, + { + "x": 18.9013824, + "y": 0.16807422, + "z": -45.4415 + }, + { + "x": 33.5459023, + "y": 4.09096456, + "z": -20.01963 + }, + { + "x": 34.1248779, + "y": 4.0770335200000005, + "z": -26.7420425 + }, + { + "x": 33.72695, + "y": 4.142160179999999, + "z": -35.1665726 + }, + { + "x": 52.81441, + "y": 0.600790583, + "z": -32.3851242 + }, + { + "x": 65.46217, + "y": 0.600790583, + "z": -42.0289726 + }, + { + "x": 72.72261, + "y": 0.600790583, + "z": -48.6636353 + }, + { + "x": 69.30476, + "y": 0.5511049777, + "z": 16.7004738 + }, + { + "x": 57.9490433, + "y": -2.10287642, + "z": 6.3532815 + }, + { + "x": 69.21042, + "y": 0.600287616, + "z": 39.88715 + }, + { + "x": 42.1991425, + "y": 4.89614344, + "z": 40.3816681 + }, + { + "x": 14.3901482, + "y": 8.633129, + "z": 39.9571266 + }, + { + "x": 18.1188564, + "y": 5.063123, + "z": 45.0536346 + }, + { + "x": 32.0923347, + "y": 5.085275, + "z": 47.6640778 + }, + { + "x": 23.9675312, + "y": 1.6643647, + "z": 56.24548 + }, + { + "x": 14.9902935, + "y": 1.54594648, + "z": 36.4069862 + } + ], + "factory4_night": [ + { + "x": -31.5008049, + "y": -2.09620523, + "z": 32.2005348 + }, + { + "x": -18.7847786, + "y": -2.097631, + "z": 40.29317 + }, + { + "x": 16.9372578, + "y": -2.08116531, + "z": 40.84489 + }, + { + "x": 23.5751019, + "y": -2.101208, + "z": 7.9283185 + }, + { + "x": 19.4345531, + "y": -2.101208, + "z": -13.3914032 + }, + { + "x": 27.6461773, + "y": -2.101208, + "z": -34.54554 + }, + { + "x": 38.79806, + "y": -2.09461427, + "z": -18.6310368 + }, + { + "x": 31.9105778, + "y": 7.618354, + "z": -32.1087 + }, + { + "x": 30.08668, + "y": 7.75526237, + "z": -20.3167381 + }, + { + "x": 21.4689655, + "y": 8.888684, + "z": 7.54276848 + }, + { + "x": 6.222929, + "y": 7.68675137, + "z": 16.6077843 + }, + { + "x": -22.1931133, + "y": 7.98713732, + "z": 11.6042461 + }, + { + "x": 1.68692517, + "y": 8.942114, + "z": -11.3421993 + }, + { + "x": -1.84703875, + "y": 0.14306390000000002, + "z": -47.23748 + }, + { + "x": 18.9013824, + "y": 0.16807422, + "z": -45.4415 + }, + { + "x": 33.5459023, + "y": 4.09096456, + "z": -20.01963 + }, + { + "x": 34.1248779, + "y": 4.0770335200000005, + "z": -26.7420425 + }, + { + "x": 33.72695, + "y": 4.142160179999999, + "z": -35.1665726 + }, + { + "x": 52.81441, + "y": 0.600790583, + "z": -32.3851242 + }, + { + "x": 65.46217, + "y": 0.600790583, + "z": -42.0289726 + }, + { + "x": 72.72261, + "y": 0.600790583, + "z": -48.6636353 + }, + { + "x": 69.30476, + "y": 0.5511049777, + "z": 16.7004738 + }, + { + "x": 57.9490433, + "y": -2.10287642, + "z": 6.3532815 + }, + { + "x": 69.21042, + "y": 0.600287616, + "z": 39.88715 + }, + { + "x": 42.1991425, + "y": 4.89614344, + "z": 40.3816681 + }, + { + "x": 14.3901482, + "y": 8.633129, + "z": 39.9571266 + }, + { + "x": 18.1188564, + "y": 5.063123, + "z": 45.0536346 + }, + { + "x": 32.0923347, + "y": 5.085275, + "z": 47.6640778 + }, + { + "x": 23.9675312, + "y": 1.6643647, + "z": 56.24548 + }, + { + "x": 14.9902935, + "y": 1.54594648, + "z": 36.4069862 + } + ], + "interchange": [ + { + "x": 211.258652, + "y": 19.1901226, + "z": -362.984436 + }, + { + "x": 237.411453, + "y": 22.1533852, + "z": -331.765869 + }, + { + "x": 273.1215, + "y": 21.8638725, + "z": -214.574951 + }, + { + "x": 273.0844, + "y": 21.86559, + "z": -118.285751 + }, + { + "x": 260.457245, + "y": 21.8928967, + "z": -43.99506 + }, + { + "x": 274.2613, + "y": 21.8254375, + "z": 74.13373 + }, + { + "x": 307.44577, + "y": 20.38458, + "z": -274.7346 + }, + { + "x": 323.356079, + "y": 21.2028351, + "z": -163.308167 + }, + { + "x": -143.090347, + "y": 27.6066227, + "z": 180.2215 + }, + { + "x": -273.3014, + "y": 22.1938782, + "z": 156.37558 + }, + { + "x": -172.691208, + "y": 21.8254375, + "z": 132.801117 + }, + { + "x": -248.8819, + "y": 21.82544, + "z": -267.4412 + }, + { + "x": -204.8294, + "y": 30.80841, + "z": -346.4929 + }, + { + "x": -175.176636, + "y": 21.8254356, + "z": -393.4588 + }, + { + "x": 22.0143661, + "y": 21.82552, + "z": -207.40477 + }, + { + "x": -34.99583, + "y": 21.8441944, + "z": -52.9352341 + }, + { + "x": -74.92484, + "y": 27.6297016, + "z": -66.27323 + }, + { + "x": -93.03051, + "y": 27.586195, + "z": -147.205643 + }, + { + "x": -144.089981, + "y": 27.5861969, + "z": -111.333084 + }, + { + "x": -75.75173, + "y": 27.5864849, + "z": 20.4922752 + }, + { + "x": 52.4439163, + "y": 27.60244, + "z": 85.6145859 + }, + { + "x": 89.78583, + "y": 21.9300022, + "z": 86.27714 + }, + { + "x": -69.2831955, + "y": 27.6003647, + "z": 136.67984 + }, + { + "x": -55.07253, + "y": 27.5864773, + "z": 104.345154 + }, + { + "x": 34.57658, + "y": 27.5864773, + "z": 110.34359 + }, + { + "x": 37.7865524, + "y": 27.6119251, + "z": 25.749609 + }, + { + "x": 85.81445, + "y": 27.58647, + "z": 27.10916 + }, + { + "x": -1.53003037, + "y": 27.5918446, + "z": -44.2807732 + }, + { + "x": 15.6643209, + "y": 27.5918446, + "z": -31.87548 + }, + { + "x": 23.9435177, + "y": 27.59439, + "z": -47.9042244 + }, + { + "x": 14.5930958, + "y": 27.6992855, + "z": -67.04211 + }, + { + "x": 5.310255, + "y": 27.6992855, + "z": -72.34979 + }, + { + "x": -1.19648051, + "y": 27.6992855, + "z": -77.7850342 + }, + { + "x": -11.9985638, + "y": 27.6992855, + "z": -74.95691 + }, + { + "x": -10.0267467, + "y": 27.6992855, + "z": -64.70458 + }, + { + "x": -13.4790888, + "y": 27.6992855, + "z": -55.763813 + }, + { + "x": -24.736517, + "y": 27.6992855, + "z": -50.5726929 + }, + { + "x": -3.94773221, + "y": 27.6491566, + "z": -13.7713146 + }, + { + "x": -77.315506, + "y": 27.5864964, + "z": -107.088562 + }, + { + "x": 99.47626, + "y": 22.989315, + "z": -56.5959473 + }, + { + "x": 50.5329361, + "y": 27.61104, + "z": -70.5485153 + }, + { + "x": 23.3646431, + "y": 37.0696449, + "z": -62.6667633 + }, + { + "x": 55.9072, + "y": 37.1878052, + "z": -63.40994 + }, + { + "x": 85.32239, + "y": 37.1878052, + "z": -61.6606178 + }, + { + "x": 92.73064, + "y": 37.1874924, + "z": -37.6910133 + }, + { + "x": 81.50103, + "y": 37.1874924, + "z": -20.139452 + }, + { + "x": 61.7250633, + "y": 37.1874924, + "z": 3.11683941 + }, + { + "x": -0.490097523, + "y": 37.06964, + "z": -19.36922 + }, + { + "x": -6.84467, + "y": 37.06964, + "z": -38.78272 + }, + { + "x": -20.2055683, + "y": 37.06964, + "z": -44.39061 + }, + { + "x": -42.8149071, + "y": 37.0696754, + "z": -36.9416771 + }, + { + "x": -4.948228, + "y": 21.8254547, + "z": 30.37021 + }, + { + "x": -20.3190212, + "y": 21.9310627, + "z": 16.1631546 + }, + { + "x": 28.15819, + "y": 21.8254566, + "z": 9.502297 + }, + { + "x": 16.8310413, + "y": 21.83612, + "z": 61.35766 + }, + { + "x": 10.7454042, + "y": 21.83612, + "z": 84.45706 + } + ], + "laboratory": [], + "lighthouse": [ + { + "x": -148.131165, + "y": 33.0202637, + "z": 232.196075 + }, + { + "x": -89.45726, + "y": 35.9800529, + "z": 412.069122 + }, + { + "x": -220.98938, + "y": 6.44640827, + "z": 417.686462 + }, + { + "x": 3.39315581, + "y": 6.457621, + "z": 273.181335 + }, + { + "x": 8.214474, + "y": 6.45759, + "z": 183.54686 + }, + { + "x": 105.231781, + "y": 1.409542, + "z": -53.10044 + }, + { + "x": 118.04454, + "y": 0.815564722, + "z": -269.901184 + }, + { + "x": -283.05658, + "y": 15.7773571, + "z": -152.031219 + }, + { + "x": -357.600861, + "y": 28.56699, + "z": -548.0032 + }, + { + "x": -177.542908, + "y": 2.881143, + "z": -361.4309 + }, + { + "x": -321.559418, + "y": 1.2295195460000001, + "z": -285.672729 + }, + { + "x": -309.780151, + "y": 1.337138832, + "z": -265.281464 + }, + { + "x": -56.02588, + "y": 12.115447, + "z": 62.83077 + }, + { + "x": -117.209175, + "y": 25.6349564, + "z": 240.5706 + }, + { + "x": -6.44019651, + "y": 6.347524, + "z": 436.3963 + }, + { + "x": -118.919609, + "y": 11.0115089, + "z": -867.547546 + }, + { + "x": 41.5410423, + "y": 11.020999, + "z": -805.78186 + }, + { + "x": 165.998413, + "y": 2.91779518, + "z": -707.2395 + }, + { + "x": -138.945068, + "y": 37.3540764, + "z": 154.4614 + }, + { + "x": 148.417374, + "y": 1.65971339, + "z": 190.729767 + }, + { + "x": 158.215378, + "y": 2.02266812, + "z": 241.3364 + } + ], + "rezervbase": [ + { + "x": 34.244854, + "y": -0.29745185399999996, + "z": -165.52475 + }, + { + "x": -81.84773, + "y": -6.57833624, + "z": -139.84668 + }, + { + "x": -70.1585541, + "y": -18.32672, + "z": 80.32257 + }, + { + "x": -141.49823, + "y": -9.90744, + "z": 64.73703 + }, + { + "x": -5.423219, + "y": 30.1348171, + "z": 168.527328 + }, + { + "x": -35.6528931, + "y": 5.86891127, + "z": 145.207581 + }, + { + "x": -193.665283, + "y": -7.07202768, + "z": -53.6095238 + }, + { + "x": -175.940628, + "y": -6.479239, + "z": 117.512894 + }, + { + "x": -32.7027626, + "y": -6.446487, + "z": 36.9128647 + }, + { + "x": -43.75431, + "y": -6.014112, + "z": 22.10809 + }, + { + "x": 33.1976471, + "y": -6.451979, + "z": -6.64785957 + } + ], + "shoreline": [ + { + "x": 370.336121, + "y": -54.12922, + "z": -130.004868 + }, + { + "x": -56.63187, + "y": -17.6429348, + "z": -266.596527 + }, + { + "x": -343.274536, + "y": -39.1471329, + "z": 183.900833 + }, + { + "x": -345.164459, + "y": -59.2554932, + "z": 450.597931 + }, + { + "x": -434.142731, + "y": -59.3447075, + "z": 466.864 + }, + { + "x": -191.904083, + "y": -64.59185, + "z": 451.90332 + }, + { + "x": -88.4936752, + "y": -47.2991676, + "z": 379.011932 + }, + { + "x": -182.476181, + "y": -46.6247444, + "z": 321.56192 + }, + { + "x": 342.920776, + "y": -52.60104, + "z": -46.3882561 + }, + { + "x": -214.563568, + "y": -17.1404762, + "z": 1.64999378 + }, + { + "x": -658.685059, + "y": -37.3970757, + "z": -16.3340988 + }, + { + "x": -57.1338921, + "y": -25.72527, + "z": -48.6768875 + }, + { + "x": 364.904449, + "y": -53.03396, + "z": -49.32578 + }, + { + "x": 319.4486, + "y": -50.95948, + "z": -44.5394478 + }, + { + "x": 192.184036, + "y": -49.3995132, + "z": -86.752594 + }, + { + "x": 124.354622, + "y": -38.5620842, + "z": -115.482368 + }, + { + "x": 143.77623, + "y": -40.975605, + "z": -205.5631 + }, + { + "x": 171.198227, + "y": -34.245285, + "z": -300.330353 + }, + { + "x": 147.1349, + "y": -37.0587044, + "z": -256.51 + }, + { + "x": 52.73984, + "y": -21.3516865, + "z": -114.782829 + }, + { + "x": 24.0976486, + "y": -21.1479015, + "z": -121.997437 + }, + { + "x": 2.49987864, + "y": -22.24748, + "z": -126.523239 + }, + { + "x": -22.1889248, + "y": -35.43923, + "z": 55.114975 + }, + { + "x": 43.18565, + "y": -42.09612, + "z": 70.2003555 + }, + { + "x": 69.8785553, + "y": -41.17338, + "z": 11.6486864 + }, + { + "x": 157.442734, + "y": -45.72659, + "z": 36.801712 + }, + { + "x": 249.083176, + "y": -45.8511772, + "z": 41.24722 + }, + { + "x": 314.49118, + "y": -47.0921364, + "z": 228.814087 + }, + { + "x": 135.580643, + "y": -51.28811, + "z": 224.899078 + }, + { + "x": 60.40513, + "y": -47.01007, + "z": 166.174133 + }, + { + "x": -45.51708, + "y": -44.6864128, + "z": 183.914246 + }, + { + "x": -184.079712, + "y": -39.3261375, + "z": 144.031677 + }, + { + "x": -231.7265, + "y": -40.5281563, + "z": 146.926376 + }, + { + "x": -290.310883, + "y": -37.85999, + "z": 139.980469 + }, + { + "x": -423.3984, + "y": -30.36368, + "z": 175.143784 + }, + { + "x": -628.9515, + "y": -46.9226151, + "z": 221.603973 + }, + { + "x": -668.3095, + "y": -32.72366, + "z": -122.797127 + }, + { + "x": -653.336365, + "y": -26.40537, + "z": -208.006943 + }, + { + "x": -689.6254, + "y": -26.4365788, + "z": -234.420242 + }, + { + "x": -638.4657, + "y": -29.4122448, + "z": -173.1854 + }, + { + "x": -626.775, + "y": -26.33351, + "z": -231.115738 + }, + { + "x": -661.849, + "y": -26.33351, + "z": -250.816208 + }, + { + "x": -428.749725, + "y": -18.16704, + "z": -290.702118 + }, + { + "x": -282.274353, + "y": 1.2894423000000002, + "z": -335.157 + }, + { + "x": -231.934784, + "y": -8.889259, + "z": -326.960846 + }, + { + "x": -148.135727, + "y": -12.004118, + "z": -343.527557 + }, + { + "x": -129.167526, + "y": -12.4445543, + "z": -202.600449 + }, + { + "x": -64.63197, + "y": -42.5674248, + "z": 125.121178 + }, + { + "x": -80.06564, + "y": -41.04094, + "z": 265.0606 + }, + { + "x": -106.219772, + "y": -59.1889839, + "z": 404.209625 + }, + { + "x": -139.264877, + "y": -63.70067, + "z": 446.948669 + }, + { + "x": -235.845688, + "y": -64.36687, + "z": 453.484344 + }, + { + "x": -281.346527, + "y": -60.0218277, + "z": 469.8063 + }, + { + "x": -319.723938, + "y": -59.37635, + "z": 447.3752 + }, + { + "x": -449.578827, + "y": -59.255497, + "z": 454.824524 + }, + { + "x": -543.087769, + "y": -59.255497, + "z": 455.2528 + }, + { + "x": -505.4898, + "y": -49.97857, + "z": 397.5128 + }, + { + "x": -582.3187, + "y": -51.8977661, + "z": 373.3344 + }, + { + "x": -705.2152, + "y": -45.48139, + "z": 281.0987 + }, + { + "x": -693.44635, + "y": -38.8620224, + "z": 207.319626 + }, + { + "x": -650.198, + "y": -42.1930466, + "z": 84.0196457 + }, + { + "x": -536.8481, + "y": -26.3908138, + "z": -247.715652 + }, + { + "x": -496.501526, + "y": -23.99046, + "z": -251.473969 + }, + { + "x": -437.921936, + "y": -19.6501274, + "z": -196.645844 + } + ], + "tarkovstreets": [], + "woods": [ + { + "x": 443.215, + "y": -6.55717945, + "z": 12.6241884 + }, + { + "x": 403.9096, + "y": -1.37851143, + "z": -149.671051 + }, + { + "x": 324.695374, + "y": 4.26850486, + "z": -193.185211 + }, + { + "x": 162.859024, + "y": 3.852831, + "z": -38.742115 + }, + { + "x": 260.721466, + "y": -0.3514566, + "z": -131.163208 + }, + { + "x": 26.7105026, + "y": 29.9298172, + "z": -293.671417 + }, + { + "x": -157.211823, + "y": 25.5068512, + "z": -601.2355 + }, + { + "x": 319.59726, + "y": 23.67145, + "z": -638.077759 + }, + { + "x": -101.737762, + "y": 13.2318125, + "z": -580.2739 + }, + { + "x": -429.1667, + "y": 9.772156, + "z": -527.866943 + }, + { + "x": -559.294739, + "y": 22.0387516, + "z": -250.421738 + }, + { + "x": -459.3182, + "y": 25.1142769, + "z": -128.3647 + }, + { + "x": -339.889862, + "y": 13.2710028, + "z": -106.506683 + }, + { + "x": -211.017868, + "y": 76.63688, + "z": -270.919 + }, + { + "x": -546.6522, + "y": 15.5616541, + "z": -211.051971 + }, + { + "x": -430.921356, + "y": 16.2786684, + "z": -186.438751 + }, + { + "x": -511.659363, + "y": 16.5106926, + "z": -176.889847 + }, + { + "x": 247.547623, + "y": -7.065467, + "z": 304.677856 + }, + { + "x": 355.4717, + "y": -0.04704280000000005, + "z": -89.83091 + }, + { + "x": 230.469925, + "y": 10.159401, + "z": -485.499939 + }, + { + "x": 81.51287, + "y": 8.78842449, + "z": -496.603058 + }, + { + "x": 311.624237, + "y": 15.281229, + "z": -670.952637 + }, + { + "x": 195.604156, + "y": 11.3580141, + "z": -592.8544 + }, + { + "x": 175.079453, + "y": 10.6330614, + "z": -551.0474 + }, + { + "x": 173.420013, + "y": 9.717274, + "z": -263.404541 + }, + { + "x": 93.9766846, + "y": 8.032005, + "z": -170.947617 + }, + { + "x": 85.3267059, + "y": -14.7274227, + "z": 110.782326 + }, + { + "x": -53.08072, + "y": -12.7061481, + "z": 131.079788 + }, + { + "x": -525.6267, + "y": 13.7922716, + "z": -423.5468 + }, + { + "x": -450.126678, + "y": 15.7312889, + "z": -408.635651 + }, + { + "x": -466.759674, + "y": 15.3880329, + "z": -337.347626 + }, + { + "x": -508.0785, + "y": 15.526474, + "z": -377.553131 + }, + { + "x": -593.815, + "y": 15.7170935, + "z": -277.6658 + }, + { + "x": -550.001038, + "y": 9.692064, + "z": -94.2853546 + }, + { + "x": -595.9386, + "y": 22.6341228, + "z": -160.671585 + }, + { + "x": -383.6546, + "y": 21.7442417, + "z": -250.893143 + }, + { + "x": -270.676941, + "y": 52.812294, + "z": -240.604126 + }, + { + "x": -231.610336, + "y": 68.10191, + "z": -229.516388 + }, + { + "x": -60.6023369, + "y": 23.7947, + "z": -264.089355 + }, + { + "x": -6.37926626, + "y": 24.2175961, + "z": -279.905823 + }, + { + "x": 59.8476677, + "y": 32.3871021, + "z": -303.686859 + }, + { + "x": 83.2094955, + "y": 23.7391167, + "z": -388.7289 + }, + { + "x": 208.758179, + "y": 16.3364058, + "z": -383.70224 + }, + { + "x": 319.0171, + "y": 11.68889, + "z": -282.5152 + }, + { + "x": 272.920654, + "y": 3.2200315, + "z": -223.00592 + }, + { + "x": 214.691162, + "y": -1.42720032, + "z": -54.0039978 + }, + { + "x": 179.347321, + "y": -3.06935477, + "z": 39.4058723 + }, + { + "x": 479.9101, + "y": -9.017019, + "z": 54.152298 + }, + { + "x": 475.3497, + "y": -11.2453747, + "z": 100.821487 + }, + { + "x": 483.88858, + "y": -17.6384125, + "z": 225.6036 + }, + { + "x": 447.803284, + "y": -18.9501457, + "z": 317.097321 + }, + { + "x": 303.408081, + "y": -8.842667, + "z": 332.040649 + }, + { + "x": 0.811879158, + "y": 1.168552756, + "z": -97.84148 + }, + { + "x": 4.99091, + "y": 15.5111284, + "z": -235.015076 + }, + { + "x": -97.36022, + "y": 26.5481014, + "z": -382.311676 + }, + { + "x": 83.64897, + "y": 19.8974457, + "z": -432.155975 + }, + { + "x": 117.491951, + "y": 9.293282, + "z": -519.5596 + }, + { + "x": 141.661743, + "y": 8.750049, + "z": -627.6664 + }, + { + "x": 298.218079, + "y": 12.9066153, + "z": -572.7863 + }, + { + "x": 300.828156, + "y": 23.3646889, + "z": -447.16864 + }, + { + "x": 242.895081, + "y": 23.58974, + "z": -429.1805 + }, + { + "x": 152.244049, + "y": 25.03806, + "z": -319.14 + }, + { + "x": 242.98175, + "y": 28.1680965, + "z": -306.249329 + }, + { + "x": 206.82666, + "y": 28.1255379, + "z": -311.781342 + }, + { + "x": -251.824371, + "y": 10.034147, + "z": -5.611849 + }, + { + "x": -284.997528, + "y": 11.5557032, + "z": -60.0407372 + }, + { + "x": -124.13118, + "y": 14.2887831, + "z": -50.3103065 + }, + { + "x": -164.177429, + "y": 24.8489475, + "z": -101.811615 + }, + { + "x": -111.149124, + "y": 26.1094646, + "z": -164.484314 + }, + { + "x": -518.632446, + "y": 8.962991, + "z": -487.8438 + }, + { + "x": -155.08374, + "y": 10.8792582, + "z": -670.128235 + }, + { + "x": -72.33275, + "y": 9.599802, + "z": -696.842957 + }, + { + "x": 83.7728958, + "y": 8.305830480000001, + "z": -668.965332 + }, + { + "x": 78.16235, + "y": 21.00355, + "z": -273.2554 + }, + { + "x": 379.620941, + "y": 17.7680054, + "z": -313.000061 + }, + { + "x": -33.33974, + "y": -3.17785859, + "z": 46.11765 + }, + { + "x": -6.830947, + "y": -3.1778574, + "z": -13.690527 + }, + { + "x": 98.41417, + "y": -1.5679554900000001, + "z": -49.0388031 + }, + { + "x": -122.815453, + "y": 8.939046, + "z": 37.5316658 + } + ], + "sandbox": [ + { + "x": 136.816422, + "y": 28.6955128, + "z": 279.7089 + }, + { + "x": 122.977036, + "y": 23.89307, + "z": 294.252075 + }, + { + "x": 119.695923, + "y": 33.34284, + "z": 267.874847 + }, + { + "x": 137.291092, + "y": 23.2307358, + "z": 212.572708 + }, + { + "x": 124.995255, + "y": 23.715498, + "z": 191.3597 + }, + { + "x": 118.501144, + "y": 29.4655933, + "z": 154.670547 + }, + { + "x": 125.156685, + "y": 29.43925, + "z": 89.35103 + }, + { + "x": 125.6253, + "y": 29.4234314, + "z": 113.45739 + }, + { + "x": 120.070488, + "y": 24.9442329, + "z": 114.421616 + }, + { + "x": -60.23611, + "y": 24.698452, + "z": 42.3922768 + }, + { + "x": -25.2352314, + "y": 24.699213, + "z": 56.53281 + }, + { + "x": -27.4792213, + "y": 30.2097416, + "z": 85.46938 + }, + { + "x": 173.531448, + "y": 17.4612217, + "z": -6.298423 + }, + { + "x": 58.77306, + "y": 31.40412, + "z": 131.0849 + }, + { + "x": 49.42541, + "y": 15.2336168, + "z": 157.316269 + }, + { + "x": 154.8506, + "y": 23.2683678, + "z": 38.88178 + }, + { + "x": 126.300125, + "y": 23.304493, + "z": 23.0072575 + }, + { + "x": -43.0123558, + "y": 24.6793537, + "z": 32.9782677 + }, + { + "x": 30.590704, + "y": 24.3751945, + "z": 35.44925 + }, + { + "x": 13.9142942, + "y": 24.35617, + "z": 95.52667 + } + ], + "sandbox_high": [ + { + "x": 136.816422, + "y": 28.6955128, + "z": 279.7089 + }, + { + "x": 122.977036, + "y": 23.89307, + "z": 294.252075 + }, + { + "x": 119.695923, + "y": 33.34284, + "z": 267.874847 + }, + { + "x": 137.291092, + "y": 23.2307358, + "z": 212.572708 + }, + { + "x": 124.995255, + "y": 23.715498, + "z": 191.3597 + }, + { + "x": 118.501144, + "y": 29.4655933, + "z": 154.670547 + }, + { + "x": 125.156685, + "y": 29.43925, + "z": 89.35103 + }, + { + "x": 125.6253, + "y": 29.4234314, + "z": 113.45739 + }, + { + "x": 120.070488, + "y": 24.9442329, + "z": 114.421616 + }, + { + "x": -60.23611, + "y": 24.698452, + "z": 42.3922768 + }, + { + "x": -25.2352314, + "y": 24.699213, + "z": 56.53281 + }, + { + "x": -27.4792213, + "y": 30.2097416, + "z": 85.46938 + }, + { + "x": 173.531448, + "y": 17.4612217, + "z": -6.298423 + }, + { + "x": 58.77306, + "y": 31.40412, + "z": 131.0849 + }, + { + "x": 49.42541, + "y": 15.2336168, + "z": 157.316269 + }, + { + "x": 154.8506, + "y": 23.2683678, + "z": 38.88178 + }, + { + "x": 126.300125, + "y": 23.304493, + "z": 23.0072575 + }, + { + "x": -43.0123558, + "y": 24.6793537, + "z": 32.9782677 + }, + { + "x": 30.590704, + "y": 24.3751945, + "z": 35.44925 + }, + { + "x": 13.9142942, + "y": 24.35617, + "z": 95.52667 + } + ] +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/Spawns/sniperSpawns.json b/user/mods/DewardianDev-MOAR/config/Spawns/sniperSpawns.json new file mode 100644 index 0000000..8fdfcd7 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/Spawns/sniperSpawns.json @@ -0,0 +1,14 @@ +{ + "bigmap": [], + "factory4_day": [], + "factory4_night": [], + "interchange": [], + "laboratory": [], + "lighthouse": [], + "rezervbase": [], + "shoreline": [], + "tarkovstreets": [], + "woods": [], + "sandbox": [], + "sandbox_high": [] +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/advancedConfig.json b/user/mods/DewardianDev-MOAR/config/advancedConfig.json new file mode 100644 index 0000000..12c1e65 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/advancedConfig.json @@ -0,0 +1,8 @@ +{ + "-------------JUST LEAVE THESE ALONE---------------": "", + "ActivateSpawnCullingOnServerStart": false, + "MarksmanDifficultyChanges": true, + "EnableBossPerformanceImprovements": true, + "SpawnpointAreaTarget": 1, + "showMapCullingDebug": false +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/bossConfig.json b/user/mods/DewardianDev-MOAR/config/bossConfig.json new file mode 100644 index 0000000..e856d78 --- /dev/null +++ b/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": 18, + "bossPartisan": 8, + "bossBully": 30 + }, + "factoryDay": { + "bossTagilla": 30 + }, + "factoryNight": { + "bossTagilla": 30 + }, + "interchange": { + "bossKilla": 30 + }, + "laboratory": {}, + "lighthouse": { + "bossKnight": 18, + "bossPartisan": 8 + }, + "rezervbase": { + "bossGluhar": 30 + }, + "shoreline": { + "bossKnight": 18, + "bossPartisan": 8, + "bossSanitar": 30 + }, + "tarkovstreets": { + "bossBoar": 30, + "bossKolontay": 30 + }, + "woods": { + "bossKojaniy": 30, + "bossKnight": 18, + "bossPartisan": 8 + }, + "gzLow": {}, + "gzHigh": { + "bossKolontay": 30 + } +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/config/config.json b/user/mods/DewardianDev-MOAR/config/config.json new file mode 100644 index 0000000..c84a59d --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/config.json @@ -0,0 +1,49 @@ +{ + "enableBotSpawning": true, + "spawnSmoothing": true, + + "pmcDifficulty": 0.6, + "scavDifficulty": 0.4, + + "scavWaveDistribution": 0.6, + "scavWaveQuantity": 1, + + "startingPmcs": false, + "pmcWaveDistribution": 0.3, + "pmcWaveQuantity": 1, + + "randomSpawns": false, + + "zombiesEnabled": false, + "zombieWaveDistribution": 0.8, + "zombieWaveQuantity": 1, + "zombieHealth": 1, + + "maxBotCap": 28, + "maxBotPerZone": 7, + + "sniperGroupChance": 0.2, + "scavGroupChance": 0.2, + "pmcGroupChance": 0.2, + + "pmcMaxGroupSize": 4, + "scavMaxGroupSize": 3, + "sniperMaxGroupSize": 1, + + "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/user/mods/DewardianDev-MOAR/config/mapConfig.json b/user/mods/DewardianDev-MOAR/config/mapConfig.json new file mode 100644 index 0000000..863c76b --- /dev/null +++ b/user/mods/DewardianDev-MOAR/config/mapConfig.json @@ -0,0 +1,185 @@ +{ + "customs": { + "sniperQuantity": 3, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 12, + "mapCullingNearPointValueScav": 22, + "spawnMinDistance": 30, + "pmcWaveCount": 12, + "scavWaveCount": 24, + "zombieWaveCount": 9, + "scavHotZones": [ + "ZoneDormitory" + ], + "pmcHotZones": [ + "ZoneDormitory" + ] + }, + "factoryDay": { + "sniperQuantity": 0, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.6, + "mapCullingNearPointValuePlayer": 2, + "mapCullingNearPointValuePmc": 2, + "mapCullingNearPointValueScav": 1, + "spawnMinDistance": 15, + "maxBotCapOverride": 12, + "maxBotPerZoneOverride": 12, + "pmcWaveCount": 8, + "scavWaveCount": 12, + "zombieWaveCount": 6 + }, + "factoryNight": { + "sniperQuantity": 0, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.6, + "mapCullingNearPointValuePlayer": 2, + "mapCullingNearPointValuePmc": 2, + "mapCullingNearPointValueScav": 1, + "spawnMinDistance": 15, + "maxBotCapOverride": 12, + "maxBotPerZoneOverride": 12, + "pmcWaveCount": 8, + "scavWaveCount": 12, + "zombieWaveCount": 6 + }, + "interchange": { + "sniperQuantity": 0, + "initialSpawnDelay": 20, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 4, + "mapCullingNearPointValueScav": 1, + "spawnMinDistance": 30, + "pmcWaveCount": 14, + "scavWaveCount": 36, + "zombieWaveCount": 12, + "scavHotZones": [ + "ZoneCenterBot", + "ZoneCenter" + ] + }, + "laboratory": { + "sniperQuantity": 0, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 3, + "mapCullingNearPointValuePmc": 3, + "mapCullingNearPointValueScav": 2, + "spawnMinDistance": 30, + "pmcWaveCount": 12, + "scavWaveCount": 0, + "zombieWaveCount": 12 + }, + "lighthouse": { + "sniperQuantity": 1, + "initialSpawnDelay": 20, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 6, + "mapCullingNearPointValuePmc": 5, + "mapCullingNearPointValueScav": 8, + "maxBotCapOverride": 22, + "spawnMinDistance": 30, + "pmcWaveCount": 12, + "scavWaveCount": 24, + "zombieWaveCount": 10, + "scavHotZones": [ + "Zone_LongRoad" + ] + }, + "rezervbase": { + "sniperQuantity": 0, + "initialSpawnDelay": 20, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 6, + "mapCullingNearPointValueScav": 1, + "spawnMinDistance": 30, + "pmcWaveCount": 11, + "scavWaveCount": 28, + "zombieWaveCount": 9, + "scavHotZones": [ + "ZoneRailStrorage" + ], + "pmcHotZones": [ + "ZoneBarrack" + ] + }, + "shoreline": { + "sniperQuantity": 3, + "initialSpawnDelay": 20, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 6, + "mapCullingNearPointValueScav": 15, + "spawnMinDistance": 30, + "pmcWaveCount": 14, + "scavWaveCount": 36, + "zombieWaveCount": 12, + "scavHotZones": [ + "ZoneSanatorium1" + ], + "pmcHotZones": [ + "ZoneSanatorium2" + ] + }, + "tarkovstreets": { + "sniperQuantity": 5, + "initialSpawnDelay": 20, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 6, + "mapCullingNearPointValueScav": 6, + "maxBotCapOverride": 18, + "spawnMinDistance": 30, + "pmcWaveCount": 10, + "scavWaveCount": 20, + "zombieWaveCount": 13 + }, + "woods": { + "sniperQuantity": 1, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.9, + "mapCullingNearPointValuePlayer": 12, + "mapCullingNearPointValuePmc": 30, + "mapCullingNearPointValueScav": 36, + "spawnMinDistance": 30, + "pmcWaveCount": 14, + "scavWaveCount": 36, + "zombieWaveCount": 10, + "scavHotZones": [ + "ZoneWoodCutter" + ], + "pmcHotZones": [ + "ZoneWoodCutter" + ] + }, + "gzLow": { + "sniperQuantity": 1, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.7, + "mapCullingNearPointValuePlayer": 6, + "mapCullingNearPointValuePmc": 5, + "mapCullingNearPointValueScav": 1, + "maxBotCapOverride": 15, + "spawnMinDistance": 15, + "pmcWaveCount": 10, + "scavWaveCount": 18, + "zombieWaveCount": 9 + }, + "gzHigh": { + "sniperQuantity": 1, + "initialSpawnDelay": 15, + "smoothingDistribution": 0.7, + "mapCullingNearPointValuePlayer": 6, + "mapCullingNearPointValuePmc": 5, + "mapCullingNearPointValueScav": 1, + "maxBotCapOverride": 15, + "spawnMinDistance": 15, + "pmcWaveCount": 12, + "scavWaveCount": 18, + "zombieWaveCount": 9 + } +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/package.json b/user/mods/DewardianDev-MOAR/package.json new file mode 100644 index 0000000..46d35c1 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/package.json @@ -0,0 +1,25 @@ +{ + "name": "MOAR", + "version": "3.1.6", + "main": "src/mod.js", + "license": "MIT", + "author": "DewardianDev", + "sptVersion": "^3.11.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" + } +} \ No newline at end of file diff --git a/user/mods/DewardianDev-MOAR/src/GlobalValues.ts b/user/mods/DewardianDev-MOAR/src/GlobalValues.ts new file mode 100644 index 0000000..a702679 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/GlobalValues.ts @@ -0,0 +1,17 @@ +import { Ixyz } from "@spt/models/eft/common/Ixyz"; +import config from "../config/config.json"; +import { + ILocationBase, + ISpawnPointParam, +} 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 = "random"; + public static addedMapZones: Record = {}; + public static indexedMapSpawns: Record = {}; + public static playerSpawn: ISpawnPointParam; +} diff --git a/user/mods/DewardianDev-MOAR/src/Routes/routes.ts b/user/mods/DewardianDev-MOAR/src/Routes/routes.ts new file mode 100644 index 0000000..6bd3e4f --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Routes/routes.ts @@ -0,0 +1,213 @@ +import { DependencyContainer } from "tsyringe"; +import { buildWaves } from "../Spawning/Spawning"; +import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import { globalValues } from "../GlobalValues"; +import { kebabToTitle } from "../utils"; +import PresetWeightingsConfig from "../../config/PresetWeightings.json"; +import { Ixyz } from "@spt/models/eft/common/Ixyz"; +import { + deleteBotSpawn, + updateBotSpawn, +} from "../SpawnZoneChanges/updateUtils"; + +export const setupRoutes = (container: DependencyContainer) => { + const staticRouterModService = container.resolve( + "StaticRouterModService" + ); + + interface AddSpawnRequest { + map: string; + position: Ixyz; + type: "player" | "pmc" | "scav" | "sniper"; + } + + staticRouterModService.registerStaticRouter( + `moarAddBotSpawn`, + [ + { + url: "/moar/addBotSpawn", + action: async ( + url: string, + req: AddSpawnRequest, + sessionID, + output + ) => { + updateBotSpawn(req.map, req.position, req.type); + return "success"; + }, + }, + ], + "moarAddBotSpawn" + ); + + staticRouterModService.registerStaticRouter( + `moarDeleteBotSpawn`, + [ + { + url: "/moar/deleteBotSpawn", + action: async ( + url: string, + req: AddSpawnRequest, + sessionID, + output + ) => { + // console.log(req); + deleteBotSpawn(req.map, req.position, req.type); + return "success"; + }, + }, + ], + "moarDeleteBotSpawn" + ); + + // 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/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/index.ts b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/index.ts new file mode 100644 index 0000000..4ab9902 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/index.ts @@ -0,0 +1,5 @@ +// context/index.js +export { default as PlayerSpawns } from "../../config/Spawns/playerSpawns.json"; +export { default as ScavSpawns } from "../../config/Spawns/scavSpawns.json"; +export { default as SniperSpawns } from "../../config/Spawns/sniperSpawns.json"; +export { default as PmcSpawns } from "../../config/Spawns/pmcSpawns.json"; diff --git a/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/setupSpawn.ts b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/setupSpawn.ts new file mode 100644 index 0000000..1c25cf1 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/setupSpawn.ts @@ -0,0 +1,243 @@ +import { DatabaseServer } from "@spt/servers/DatabaseServer"; +import { configLocations, originalMapList } from "../Spawning/constants"; +import { DependencyContainer } from "tsyringe"; +import mapConfig from "../../config/mapConfig.json"; +import advancedConfig from "../../config/advancedConfig.json"; +import { ISpawnPointParam } from "@spt/models/eft/common/ILocationBase"; +import { globalValues } from "../GlobalValues"; +import { + AddCustomBotSpawnPoints, + BuildCustomPlayerSpawnPoints, + AddCustomPmcSpawnPoints, + AddCustomSniperSpawnPoints, + cleanClosest, + getClosestZone, + removeClosestSpawnsFromCustomBots, +} from "../Spawning/spawnZoneUtils"; +import { shuffle } from "../Spawning/utils"; +import { PlayerSpawns, PmcSpawns, ScavSpawns, SniperSpawns } from "."; +import { updateAllBotSpawns } from "./updateUtils"; +import { showMapCullingDebug } from "../../config/advancedConfig.json" + +export const setupSpawns = (container: DependencyContainer) => { + const databaseServer = container.resolve("DatabaseServer"); + const { locations } = databaseServer.getTables(); + + const indexedMapSpawns: Record = {}; + + const mapsToExcludeFromPlayerCulling = new Set([ + "factory4_day", + "factory4_night", + "laboratory", + ]); + + originalMapList.forEach((map, mapIndex) => { + const configMap = configLocations[mapIndex]; + const allZones = [ + ...new Set( + locations[map].base.SpawnPointParams.filter( + ({ BotZoneName }: ISpawnPointParam) => !!BotZoneName + ).map(({ BotZoneName }: ISpawnPointParam) => BotZoneName) + ), + ]; + + locations[map].base.OpenZones = allZones.join(","); + + let bossSpawns: ISpawnPointParam[] = []; + let scavSpawns: ISpawnPointParam[] = []; + let sniperSpawns: ISpawnPointParam[] = []; + + let pmcSpawns: ISpawnPointParam[] = []; + + const bossZoneList = new Set([ + "Zone_Blockpost", + "Zone_RoofRocks", + "Zone_RoofContainers", + "Zone_RoofBeach", + "Zone_TreatmentRocks", + "Zone_TreatmentBeach", + "Zone_Hellicopter", + "Zone_Island", + "BotZoneGate1", + "BotZoneGate2", + "BotZoneBasement", + ]); + + const isGZ = map.includes("sandbox"); + + shuffle(locations[map].base.SpawnPointParams).forEach( + (point) => { + switch (true) { + case point.Categories.includes("Boss") || + bossZoneList.has(point.BotZoneName): + bossSpawns.push(point); + break; + + case point.BotZoneName?.toLowerCase().includes("snipe") || + (map !== "lighthouse" && point.DelayToCanSpawnSec > 40): + sniperSpawns.push(point); + break; + + case !!point.Infiltration || point.Categories.includes("Coop"): + pmcSpawns.push(point); + break; + default: + scavSpawns.push(point); + break; + } + } + ); + + // fix GZ + if (isGZ) { + sniperSpawns.map((point, index) => { + if (index < 2) { + point.BotZoneName = Math.random() + ? "ZoneSandSnipeCenter" + : "ZoneSandSnipeCenter2"; + } else { + point.BotZoneName = ["ZoneSandSnipeCenter", "ZoneSandSnipeCenter2"][ + index + ]; + } + return point; + }); + } + + if (advancedConfig.ActivateSpawnCullingOnServerStart) { + ScavSpawns[map] = + removeClosestSpawnsFromCustomBots( + ScavSpawns, + scavSpawns, + map, + configLocations[mapIndex] + ) || []; + PmcSpawns[map] = + removeClosestSpawnsFromCustomBots( + PmcSpawns, + pmcSpawns, + map, + configLocations[mapIndex] + ) || []; + PlayerSpawns[map] = + removeClosestSpawnsFromCustomBots( + PlayerSpawns, + pmcSpawns, + map, + configLocations[mapIndex] + ) || []; + SniperSpawns[map] = + removeClosestSpawnsFromCustomBots( + SniperSpawns, + sniperSpawns, + map, + configLocations[mapIndex] + ) || []; + } + + const { spawnMinDistance: limit } = mapConfig[configLocations[mapIndex]]; + + let playerSpawns = BuildCustomPlayerSpawnPoints( + map, + locations[map].base.SpawnPointParams + ); + + playerSpawns = cleanClosest( + playerSpawns, + mapIndex, + mapConfig[configMap].mapCullingNearPointValuePlayer + ); + + scavSpawns = cleanClosest( + AddCustomBotSpawnPoints(scavSpawns, map), + mapIndex, + mapConfig[configMap].mapCullingNearPointValueScav + ).map((point, botIndex) => { + if (point.ColliderParams?._props?.Radius < limit) { + point.ColliderParams._props.Radius = limit; + } + + return !!point.Categories.length + ? { + ...point, + BotZoneName: isGZ ? "ZoneSandbox" : point?.BotZoneName, + Categories: ["Bot"], + Sides: ["Savage"], + CorePointId: 1, + } + : point; + }); + + pmcSpawns = cleanClosest( + AddCustomPmcSpawnPoints(pmcSpawns, map), + mapIndex, + mapConfig[configMap].mapCullingNearPointValuePmc + ).map((point, pmcIndex) => { + if (point.ColliderParams?._props?.Radius < limit) { + point.ColliderParams._props.Radius = limit; + } + + return !!point.Categories.length + ? { + ...point, + BotZoneName: isGZ + ? "ZoneSandbox" + : getClosestZone( + scavSpawns, + point.Position.x, + point.Position.y, + point.Position.z + ), + Categories: ["Coop", Math.random() ? "Group" : "Opposite"], + Sides: ["Pmc"], + CorePointId: 0, + } + : point; + }); + + sniperSpawns = AddCustomSniperSpawnPoints(sniperSpawns, map); + + indexedMapSpawns[mapIndex] = [ + ...sniperSpawns.map((point) => ({ ...point, type: "sniper" })), + ...bossSpawns.map((point) => ({ ...point, type: "boss" })), + ...scavSpawns.map((point) => ({ ...point, type: "scav" })), + ...pmcSpawns.map((point) => ({ ...point, type: "pmc" })), + ...playerSpawns.map((point) => ({ ...point, type: "player" })), + ]; + showMapCullingDebug && + console.log( + "sniperSpawns", + sniperSpawns.length, + "bossSpawns", + bossSpawns.length, + "scavSpawns", + scavSpawns.length, + "pmcSpawns", + pmcSpawns.length, + "playerSpawns", + playerSpawns.length, + map + ); + + locations[map].base.SpawnPointParams = indexedMapSpawns[mapIndex]; + + const listToAddToOpenZones = [ + ...new Set( + locations[map].base.SpawnPointParams.map( + ({ BotZoneName }) => BotZoneName + ).filter((zone) => !!zone) + ), + ]; + + locations[map].base.OpenZones = listToAddToOpenZones.join(","); + }); + + // PlayerSpawns, PmcSpawns, ScavSpawns, SniperSpawns + if (advancedConfig.ActivateSpawnCullingOnServerStart) { + updateAllBotSpawns(PlayerSpawns, "playerSpawns"); + updateAllBotSpawns(PmcSpawns, "pmcSpawns"); + updateAllBotSpawns(ScavSpawns, "scavSpawns"); + updateAllBotSpawns(SniperSpawns, "sniperSpawns"); + } + globalValues.indexedMapSpawns = indexedMapSpawns; +}; diff --git a/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/updateUtils.ts b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/updateUtils.ts new file mode 100644 index 0000000..899047e --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/SpawnZoneChanges/updateUtils.ts @@ -0,0 +1,112 @@ +import { Ixyz } from "@spt/models/eft/common/Ixyz"; +import { getDistance } from "../Spawning/spawnZoneUtils"; + +const fs = require("fs"); +const path = require("path"); +const currentDirectory = process.cwd(); +// Function to update JSON file +export const updateJsonFile = ( + filePath: string, + callback: (jsonData) => void, + successMessage: string +) => { + // Read the JSON file + fs.readFile(filePath, "utf8", (err, data) => { + if (err) { + console.error("Error reading the file:", err); + return; + } + + // Parse the JSON data + let jsonData; + try { + jsonData = JSON.parse(data); + } catch (parseError) { + console.error("Error parsing JSON data:", parseError); + return; + } + + callback(jsonData); + + // Update the JSON object + + // Write the updated JSON object back to the file + fs.writeFile( + filePath, + JSON.stringify(jsonData, null, 2), + "utf8", + (writeError) => { + if (writeError) { + console.error("Error writing the file:", writeError); + return; + } + + console.log(successMessage); + } + ); + }); +}; + +export const updateBotSpawn = ( + map: string, + value: Ixyz, + type: "player" | "pmc" | "scav" | "sniper" +) => { + map = map.toLowerCase(); + updateJsonFile( + `${currentDirectory}/user/mods/DewardianDev-MOAR/config/Spawns/${type}Spawns.json`, + (jsonData) => { + value.y = value.y + 0.5; + if (jsonData[map]) { + jsonData[map].push(value); + } else { + jsonData[map] = [value]; + } + }, + "Successfully added one bot spawn to " + map + ); +}; + +export const deleteBotSpawn = ( + map: string, + value: Ixyz, + type: "player" | "pmc" | "scav" | "sniper" +) => { + map = map.toLowerCase(); + updateJsonFile( + `${currentDirectory}/user/mods/DewardianDev-MOAR/config/Spawns/${type}Spawns.json`, + (jsonData) => { + if (jsonData[map]) { + const { x: X, y: Y, z: Z } = value; + let nearest = undefined; + let nearDist = Infinity; + jsonData[map].forEach(({ x, y, z }, index) => { + const dist = getDistance(x, y, z, X, Y, Z); + if (dist < nearDist) { + nearest = index; + nearDist = dist; + } + }); + + if (nearest) { + (jsonData[map] as Ixyz[]).splice(nearest, 1); + } else { + console.log("No nearest spawn on " + map); + } + } + }, + "Successfully removed one bot spawn from " + ); +}; + +export const updateAllBotSpawns = ( + values: Record, + targetType: string +) => + updateJsonFile( + `${currentDirectory}/user/mods/DewardianDev-MOAR/config/Spawns/${targetType}.json`, + (jsonData) => { + Object.keys(jsonData).forEach((map) => (jsonData[map] = values[map])); + }, + "Successfully updated all Spawns" + ); diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts b/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts new file mode 100644 index 0000000..ba2b6a7 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/Spawning.ts @@ -0,0 +1,173 @@ +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, + saveToFile, +} 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 { enforceSmoothing, setEscapeTimeOverrides } from "./utils"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import updateSpawnLocations from "./updateSpawnLocations"; +import marksmanChanges from "./marksmanChanges"; +import advancedConfig from "../../config/advancedConfig.json"; + +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 } = 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.forcedPreset + ? globalValues.forcedPreset + : 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.removeExistingPmcWaves = true; + + Object.keys(pmcConfig.customPmcWaves).forEach(key => { + pmcConfig.customPmcWaves[key] = [] + }) + + + if (config.startingPmcs && (!config.randomSpawns || config.spawnSmoothing)) { + Logger.warning( + `[MOAR] Starting pmcs turned on, turning off cascade system and smoothing.\n` + ); + config.spawnSmoothing = false; + config.randomSpawns = true; + } + + if (advancedConfig.MarksmanDifficultyChanges) { + marksmanChanges(bots); + } + + updateSpawnLocations(locationList, config); + + setEscapeTimeOverrides(locationList, _mapConfig, Logger, config); + + // BOSS RELATED STUFF! + buildBossWaves(config, locationList); + + //Zombies + if (config.zombiesEnabled) { + buildZombieWaves(config, locationList, bots); + } + + buildPmcs(config, locationList); + + // Make main waves + buildScavMarksmanWaves(config, locationList, botConfig); + + // enableSmoothing + if (config.spawnSmoothing) { + enforceSmoothing(locationList); + } + + // saveToFile(locations.bigmap.base.SpawnPointParams, "spawns.json"); + + 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/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts b/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts new file mode 100644 index 0000000..a269434 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/buildBossWaves.ts @@ -0,0 +1,341 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import bossConfig from "../../config/bossConfig.json"; +import advancedConfig from "../../config/advancedConfig.json"; +import mapConfig from "../../config/mapConfig.json"; +import { + bossesToRemoveFromPool, + bossPerformanceHash, + 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) + ); + + // Performance changes + if (advancedConfig.EnableBossPerformanceImprovements) { + locationList[indx].base.BossLocationSpawn.forEach((Boss, bIndex) => { + if (Boss.BossChance < 1) return; + if (!!bossPerformanceHash[Boss.BossName || ""]) { + + const varsToUpdate: Record = + bossPerformanceHash[Boss.BossName]; + // make it so bossPartisan has a random spawn time + if (Boss.BossName === "bossPartisan") { + const max = locationList[ + indx + ].base.EscapeTimeLimit + + varsToUpdate.Time = Math.floor(Math.random() * 50 * max) + // console.log(varsToUpdate, max * 60) + } + + locationList[indx].base.BossLocationSpawn[bIndex] = { + ...Boss, + ...varsToUpdate, + }; + } + }); + } + + 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. + + const duplicateBosses = [ + ...locationList[key].base.BossLocationSpawn.filter( + ({ BossName, BossZone }) => bossList.includes(BossName) + ).map(({ BossName }) => BossName), + "bossKnight", // So knight doesn't invade + ]; + + //Build bosses to add + const bossesToAdd = shuffle(Object.values(bosses)) + .filter(({ BossName }) => !duplicateBosses.includes(BossName)) + .map((boss, j) => ({ + ...boss, + BossZone: "", + 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 from bossConfig.json --- ` + ); + 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)}` + ); + // console.log(locationList[index].base.BossLocationSpawn.length); + + const bossesToSkip = new Set(["sectantPriest", "pmcBot"]); + // 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.map( + ({ BossChance, BossName, TriggerId }, bossIndex) => { + if (BossChance < 1) { + return locationList[index].base.BossLocationSpawn[bossIndex]; + } + if ( + !TriggerId && + !bossesToSkip.has(BossName) && + BossChance < 100 + ) { + if ( + BossChance / 100 < Math.random()) { + locationList[index].base.BossLocationSpawn[ + bossIndex + ].BossChance = 0; + + locationList[index].base.BossLocationSpawn[bossIndex].ForceSpawn = + false; + + locationList[index].base.BossLocationSpawn[ + bossIndex + ].IgnoreMaxBots = false; + } else { + locationList[index].base.BossLocationSpawn[ + bossIndex + ].BossChance = 100; + } + } + return locationList[index].base.BossLocationSpawn[bossIndex]; + } + ).filter(({ BossChance, BossName, ...rest }) => { + if (BossChance < 1) { + return false; + } + return true + }); + + // if (mapName === "lighthouse") { + // console.log( + // locationList[index].base.BossLocationSpawn.map( + // ({ BossName, BossChance }) => ({ BossName, BossChance }) + // ) + // ); + // } + + }); + + if (hasChangedBossSpawns) { + console.log( + `[MOAR]: --- Adjusting default boss spawn rates complete --- \n` + ); + } + } +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts b/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts new file mode 100644 index 0000000..c2416b2 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/buildPmcs.ts @@ -0,0 +1,142 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { defaultEscapeTimes, defaultHostility } from "./constants"; +import { buildBotWaves, looselyShuffle, MapSettings, shuffle } from "./utils"; +import { saveToFile } from "../utils"; +import getSortedSpawnPointList from "./spawnZoneUtils"; +import { globalValues } from "../GlobalValues"; + +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]; + + // Set pmcs hostile to everything + locationList[index].base.BotLocationModifier.AdditionalHostilitySettings = + defaultHostility; + + const { + pmcHotZones = [], + pmcWaveCount, + initialSpawnDelay, + } = (mapConfig?.[map] as MapSettings) || {}; + + const { + Position: { x, y, z }, + } = globalValues.playerSpawn; + + let pmcZones = getSortedSpawnPointList( + locationList[index].base.SpawnPointParams.filter( + (point) => point["type"] === "pmc" + ), + x, + y, + z, + 0.05 + ).map(({ BotZoneName }) => BotZoneName); + + looselyShuffle(pmcZones, 3); + + // console.log(pmcZones); + + if (map === "laboratory") { + pmcZones = new Array(10).fill(pmcZones).flat(1); + } + + if (config.randomSpawns) pmcZones = shuffle(pmcZones); + + const escapeTimeLimitRatio = Math.round( + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] + ); + + let totalWaves = Math.round( + pmcWaveCount * config.pmcWaveQuantity * escapeTimeLimitRatio + ); + + if (!!pmcHotZones.length && totalWaves > 0) { + totalWaves = totalWaves + pmcHotZones.length; + } + + while (totalWaves - pmcZones.length > 0) { + console.log( + `${map} ran out of appropriate zones for pmcs, duplicating zones` + ); + // const addEmpty = new Array(numberOfZoneless).fill(""); + pmcZones = [...pmcZones, ...pmcZones]; + if (pmcZones.length === 0) { + 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 timeLimit = locationList[index].base.EscapeTimeLimit * 60; + + const half = Math.round( + totalWaves % 2 === 0 ? totalWaves / 2 : (totalWaves + 1) / 2 + ); + + const usecSpawns = pmcZones.filter((_, i) => i % 2 === 0); + const bearSpawns = pmcZones.filter((_, i) => i % 2 !== 0); + + const pmcUSEC = buildBotWaves( + half, + config.startingPmcs ? Math.round(0.2 * timeLimit) : timeLimit, + config.pmcMaxGroupSize - 1, + config.pmcGroupChance, + usecSpawns, + config.pmcDifficulty, + "pmcUSEC", + false, + config.pmcWaveDistribution, + initialSpawnDelay + Math.round(10 * Math.random()) + ); + + const pmcBEAR = buildBotWaves( + half, + config.startingPmcs ? Math.round(0.1 * timeLimit) : timeLimit, + config.pmcMaxGroupSize - 1, + config.pmcGroupChance, + bearSpawns, + config.pmcDifficulty, + "pmcBEAR", + false, + config.pmcWaveDistribution, + initialSpawnDelay + Math.round(10 * Math.random()) + ); + + const pmcs = [...pmcUSEC, ...pmcBEAR]; + // console.log(pmcs.map(({ Time }) => Time)); + if (pmcs.length) { + // Add hotzones if exist + pmcHotZones.forEach((hotzone) => { + const index = Math.floor(pmcs.length * Math.random()); + pmcs[index].BossZone = hotzone; + // console.log(pmcs[index]); + }); + } + + // console.log( + // map, + // pmcs.map(({ BossZone }) => BossZone) + // ); + + locationList[index].base.BossLocationSpawn = [ + ...pmcs, + ...locationList[index].base.BossLocationSpawn, + ]; + } +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts b/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts new file mode 100644 index 0000000..228b81c --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/buildScavMarksmanWaves.ts @@ -0,0 +1,231 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { defaultEscapeTimes, originalMapList } from "./constants"; +import { buildBotWaves, looselyShuffle, MapSettings, shuffle } from "./utils"; +import { WildSpawnType } from "@spt/models/eft/common/ILocationBase"; +import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; +import { saveToFile } from "../utils"; +import getSortedSpawnPointList from "./spawnZoneUtils"; +import { globalValues } from "../GlobalValues"; + +export default function buildScavMarksmanWaves( + config: typeof _config, + locationList: ILocation[], + botConfig: IBotConfig +) { + let { + maxBotCap, + scavWaveQuantity, + scavWaveDistribution, + sniperMaxGroupSize, + maxBotPerZone, + scavMaxGroupSize, + scavDifficulty, + sniperGroupChance, + scavGroupChance, + } = 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.waves = []; + 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 = [], + sniperQuantity = 1, + scavWaveCount, + initialSpawnDelay, + } = (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; + locationList[index].base.BotMaxPlayer = 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, DelayToCanSpawnSec, BotZoneName, Sides }) => + // !Categories.includes("Boss") && + // Sides[0] === "Savage" && + // (BotZoneName?.toLowerCase().includes("snipe") || + // DelayToCanSpawnSec > 40) + // ) + // .map(({ BotZoneName }) => BotZoneName || "") + // ); + + const { + Position: { x, y, z }, + } = globalValues.playerSpawn; + + const sniperSpawns = getSortedSpawnPointList( + locationList[index].base.SpawnPointParams.filter( + (point) => point["type"] === "sniper" + ), + x, + y, + z + ); + + let sniperLocations = sniperSpawns.map(({ BotZoneName }) => BotZoneName); + // console.log(sniperLocations); + + const sniperDelay = 25; + // Make sure that the sniper spawns permit snipers to actually spawn early. + const sniperIds = new Set(sniperSpawns.map(({ Id }) => Id)); + + locationList[index].base.SpawnPointParams.forEach((point, snipeIndex) => { + if (sniperIds.has(point.Id)) { + locationList[index].base.SpawnPointParams[ + snipeIndex + ].DelayToCanSpawnSec = 20; + } + }); + + if (sniperLocations.length) { + locationList[index].base.MinMaxBots = [ + { + WildSpawnType: "marksman", + max: sniperLocations.length * 5, + min: sniperLocations.length, + }, + ]; + } + + let scavZones = getSortedSpawnPointList( + locationList[index].base.SpawnPointParams.filter( + (point) => point["type"] === "scav" + ), + x, + y, + z, + 0.05 + ).map(({ BotZoneName }) => BotZoneName); + + looselyShuffle(scavZones, 3); + + const escapeTimeLimitRatio = Math.round( + locationList[index].base.EscapeTimeLimit / defaultEscapeTimes[map] + ); + + // Scavs + let scavTotalWaveCount = Math.round( + scavWaveCount * scavWaveQuantity * escapeTimeLimitRatio + ); + + if (scavHotZones.length && scavTotalWaveCount > 0) { + scavTotalWaveCount = scavTotalWaveCount + scavHotZones.length; + } + + while (scavTotalWaveCount - scavZones.length > 0) { + console.log( + `${map} ran out of appropriate zones for scavs, duplicating zones` + ); + // const addEmpty = new Array(numberOfZoneless).fill(""); + scavZones = [...scavZones, ...scavZones]; + if (scavZones.length === 0) { + 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; + + // if (config.randomSpawns) + // sniperLocations = shuffle(sniperLocations); + // console.log(map); + const snipers = buildBotWaves( + Math.min(sniperQuantity, sniperLocations.length), + timeLimit, ///30, + sniperMaxGroupSize, + sniperGroupChance, + sniperLocations, + 0.8, + WildSpawnType.MARKSMAN, + true, + 0.3, + sniperDelay + ); + + if (config.randomSpawns) scavZones = shuffle(scavZones); + const scavWaves = buildBotWaves( + scavTotalWaveCount, + timeLimit, + scavMaxGroupSize, + scavGroupChance, + scavZones, + scavDifficulty, + WildSpawnType.ASSAULT, + false, + scavWaveDistribution, + initialSpawnDelay + Math.round(10 * Math.random()) + ); + + // Add hotzones if exist + if (scavWaves.length) { + scavHotZones.forEach((hotzone) => { + const index = Math.floor(scavWaves.length * Math.random()); + scavWaves[index].BossZone = hotzone; + // console.log(scavWaves[index].BossZone); + }); + } + // if (map === "shoreline") console.log(scavWaves.map(({ Time }) => Time)); + // console.log(snipers, scavWaves) + locationList[index].base.BossLocationSpawn = [ + ...snipers, + ...scavWaves, + ...locationList[index].base.BossLocationSpawn, + ]; + } +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts b/user/mods/DewardianDev-MOAR/src/Spawning/buildZombieWaves.ts new file mode 100644 index 0000000..2550193 --- /dev/null +++ b/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 * 60, + zombieWaveDistribution, + 9999 + ); + + debug && + console.log( + configLocations[indx], + " generated ", + zombieWaves.length, + "Zombies" + ); + + location.BossLocationSpawn.push(...zombieWaves); + + // console.log(zombieWaves[0], zombieWaves[7]); + } +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts b/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts new file mode 100644 index 0000000..19b37f6 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/constants.ts @@ -0,0 +1,232 @@ +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", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "assault", + "marksman", + "pmcUSEC", + "exUsec", + "pmcBot", + "bossBully", + ], + AlwaysFriends: [ + "bossZryachiy", + "followerZryachiy", + "peacefullZryachiyEvent", + "ravangeZryachiyEvent", + "gifter", + ], + BearEnemyChance: 100, + BearPlayerBehaviour: "AlwaysEnemies", + BotRole: "pmcBEAR", + ChancedEnemies: [], + Neutral: ["shooterBTR"], + 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", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "assault", + "marksman", + "pmcBEAR", + "exUsec", + "pmcBot", + "bossBully", + ], + AlwaysFriends: [ + "bossZryachiy", + "followerZryachiy", + "peacefullZryachiyEvent", + "ravangeZryachiyEvent", + "gifter", + ], + BearEnemyChance: 100, + BearPlayerBehaviour: "AlwaysEnemies", + BotRole: "pmcUSEC", + ChancedEnemies: [], + Neutral: ["shooterBTR"], + 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, +}; + +export const bossPerformanceHash = { + bossZryachiy: { + BossChance: 50, + BossEscortAmount: "0", + }, + exUsec: { + BossEscortAmount: "1", + BossChance: 40, + }, + bossBully: { + BossEscortAmount: "2,3", + }, + bossBoar: { + BossEscortAmount: "1,2,2,2", + }, + bossBoarSniper: { + BossEscortAmount: "1", + }, + bossKojaniy: { + BossEscortAmount: "1,2,2", + }, + bossPartisan: { + TriggerId: "", + TriggerName: "", + RandomTimeSpawn: false, + Time:120, + }, + // bossSanitar: { + // BossEscortAmount: "1,2,3", + // }, +}; diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/marksmanChanges.ts b/user/mods/DewardianDev-MOAR/src/Spawning/marksmanChanges.ts new file mode 100644 index 0000000..5fb91cc --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/marksmanChanges.ts @@ -0,0 +1,31 @@ +import { IDifficultyCategories } from "@spt/models/eft/common/tables/IBotType"; +import { IBots } from "@spt/models/spt/bots/IBots"; +import { saveToFile } from "../utils"; + +export default function marksmanChanges(bots: IBots) { + // saveToFile(bots.types.marksman.difficulty, "marksmanDifficulty.json"); + for (const diff in bots.types.marksman.difficulty) { + (bots.types.marksman.difficulty[diff] as IDifficultyCategories).Core = { + ...bots.types.marksman.difficulty[diff].Core, + VisibleAngle: 300, + VisibleDistance: 245, + ScatteringPerMeter: 0.1, + HearingSense: 2.85, + }; + + (bots.types.marksman.difficulty[diff] as IDifficultyCategories).Mind = { + ...bots.types.marksman.difficulty[diff].Mind, + BULLET_FEEL_DIST: 360, + CHANCE_FUCK_YOU_ON_CONTACT_100: 10, + }; + + (bots.types.marksman.difficulty[diff] as IDifficultyCategories).Hearing = { + ...bots.types.marksman.difficulty[diff].Hearing, + CHANCE_TO_HEAR_SIMPLE_SOUND_0_1: 0.7, + DISPERSION_COEF: 3.6, + CLOSE_DIST: 10, + FAR_DIST: 30, + }; + } + // saveToFile(bots.types.marksman.difficulty, "marksmanDifficulty2.json"); +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/spawnZoneUtils.ts b/user/mods/DewardianDev-MOAR/src/Spawning/spawnZoneUtils.ts new file mode 100644 index 0000000..55ae82a --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/spawnZoneUtils.ts @@ -0,0 +1,410 @@ +import _config from "../../config/config.json"; +import { ISpawnPointParam } from "@spt/models/eft/common/ILocationBase"; +import mapConfig from "../../config/mapConfig.json"; +import { Ixyz } from "@spt/models/eft/common/Ixyz"; +import { configLocations } from "./constants"; +import { + ScavSpawns, + PlayerSpawns, + SniperSpawns, + PmcSpawns, +} from "../SpawnZoneChanges"; +import { MapConfigType } from "./utils"; + +function sq(n: number) { + return n * n; +} + +function pt(a: number, b: number) { + return Math.sqrt(sq(a) + sq(b)); +} + +export const getDistance = ( + x: number, + y: number, + z: number, + mX: number, + mY: number, + mZ: number +) => { + (x = Math.abs(x - mX)), (y = Math.abs(y - mY)), (z = Math.abs(z - mZ)); + + return pt(pt(x, z), y); +}; + +export default function getSortedSpawnPointList( + SpawnPointParams: ISpawnPointParam[], + mX: number, + my: number, + mZ: number, + cull?: number +): ISpawnPointParam[] { + let culledAmount = 0; + + const sorted = SpawnPointParams.sort((a, b) => { + const a1 = getDistance( + a.Position.x, + a.Position.y, + a.Position.z, + mX, + my, + mZ + ); + const b1 = getDistance( + b.Position.x, + b.Position.y, + b.Position.z, + mX, + my, + mZ + ); + return a1 - b1; + }).filter((_, index) => { + if (!cull) return true; + const result = index > SpawnPointParams.length * cull; + if (!result) culledAmount++; + + return result; + }); + + if (_config.debug && culledAmount > 0) { + console.log( + "Reduced to " + + Math.round((sorted.length / SpawnPointParams.length) * 100) + + "% of original spawns", + SpawnPointParams.length, + ">", + sorted.length, + "\n" + ); + } + return sorted; +} + +export function cleanClosest( + SpawnPointParams: ISpawnPointParam[], + mapIndex: number, + mapCullingNearPointValue: number +): ISpawnPointParam[] { + const map = configLocations[mapIndex]; + + const okayList = new Set(); + const filteredParams = SpawnPointParams.map((point) => { + const { + Position: { x: X, y: Y, z: Z }, + } = point; + const result = !SpawnPointParams.some(({ Position: { z, x, y }, Id }) => { + const dist = getDistance(X, Y, Z, x, y, z); + return mapCullingNearPointValue > dist && dist !== 0 && !okayList.has(Id); + }); + + if (!result) { + okayList.add(point.Id); + } + + return result + ? point + : { + ...point, + DelayToCanSpawnSec: 9999999, + CorePointId: 99999, + Categories: [], + Sides: [], + }; + }); + + if (_config.debug) { + const actualCulled = filteredParams.filter( + ({ Categories }) => !!Categories.length + ); + console.log( + map, + filteredParams.length, + ">", + actualCulled.length, + "Reduced to " + + Math.round((actualCulled.length / filteredParams.length) * 100) + + "% of original spawns", + // player ? "player" : "bot" + ); // high, low} + } + + return filteredParams.filter((point) => !!point.Categories.length); + + // if (!_config.debug) { + // const actualCulled = culled.filter(({ Categories }) => !!Categories.length); + // console.log( + // map, + // "Reduced to " + + // Math.round((actualCulled.length / culled.length) * 100) + + // "% of original spawns", + // culled.length, + // ">", + // actualCulled.length + // // "\n" + // ); // high, low} + // } +} + +export function uuidv4() { + return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => + ( + +c ^ + (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4))) + ).toString(16) + ); +} + +export const AddCustomPmcSpawnPoints = ( + SpawnPointParams: ISpawnPointParam[], + map: string +) => { + if (!PmcSpawns[map] || !PmcSpawns[map].length) { + _config.debug && console.log("no custom Bot spawns for " + map); + return SpawnPointParams; + } + + const playerSpawns = PmcSpawns[map].map((coords: Ixyz, index: number) => ({ + BotZoneName: getClosestZone(SpawnPointParams, coords.x, coords.y, coords.z), + Categories: ["Coop", Math.random() ? "Group" : "Opposite"], + Sides: ["Pmc"], + CorePointId: 0, + ColliderParams: { + _parent: "SpawnSphereParams", + _props: { + Center: { + x: 0, + y: 0, + z: 0, + }, + Radius: 20, + }, + }, + DelayToCanSpawnSec: 4, + Id: uuidv4(), + Infiltration: "", + Position: coords, + Rotation: random360(), + })); + + return [...SpawnPointParams, ...playerSpawns]; +}; + +export const AddCustomBotSpawnPoints = ( + SpawnPointParams: ISpawnPointParam[], + map: string +) => { + if (!ScavSpawns[map] || !ScavSpawns[map].length) { + _config.debug && console.log("no custom Bot spawns for " + map); + return SpawnPointParams; + } + + const scavSpawns = ScavSpawns[map].map((coords: Ixyz) => ({ + BotZoneName: getClosestZone(SpawnPointParams, coords.x, coords.y, coords.z), + Categories: ["Bot"], + ColliderParams: { + _parent: "SpawnSphereParams", + _props: { + Center: { + x: 0, + y: 0, + z: 0, + }, + Radius: 20, + }, + }, + CorePointId: 1, + DelayToCanSpawnSec: 4, + Id: uuidv4(), + Infiltration: "", + Position: coords, + Rotation: random360(), + Sides: ["Savage"], + })); + + return [...SpawnPointParams, ...scavSpawns]; +}; + +export const AddCustomSniperSpawnPoints = ( + SpawnPointParams: ISpawnPointParam[], + map: string +) => { + if (!SniperSpawns[map] || !SniperSpawns[map].length) { + _config.debug && console.log("no custom Player spawns for " + map); + return SpawnPointParams; + } + + const sniperSpawns = SniperSpawns[map].map((coords: Ixyz, index: number) => ({ + BotZoneName: + getClosestZone(SpawnPointParams, coords.x, coords.y, coords.z) || + "custom_snipe_" + index, + Categories: ["Bot"], + ColliderParams: { + _parent: "SpawnSphereParams", + _props: { + Center: { + x: 0, + y: 0, + z: 0, + }, + Radius: 20, + }, + }, + CorePointId: 1, + DelayToCanSpawnSec: 4, + Id: uuidv4(), + Infiltration: "", + Position: coords, + Rotation: random360(), + Sides: ["Savage"], + })); + + return [...SpawnPointParams, ...sniperSpawns]; +}; + +export const random360 = () => Math.random() * 360; + +export const BuildCustomPlayerSpawnPoints = ( + map: string, + refSpawns: ISpawnPointParam[] +) => { + const playerOnlySpawns = refSpawns + .filter((item) => !!item.Infiltration && item.Categories[0] === "Player") + .map((point) => { + point.ColliderParams._props.Radius = 1; + point.Position.y = point.Position.y + 0.5; + return { + ...point, + BotZoneName: "", + isCustom: true, + Id: uuidv4(), + Sides: ["Pmc"], + }; + }); + + // console.log(map, playerOnlySpawns.length); + if (!PlayerSpawns[map] || !PlayerSpawns[map].length) { + _config.debug && console.log("no custom Player spawns for " + map); + return playerOnlySpawns; + } + + const getClosestInfil = (X: number, Y: number, Z: number) => { + let closest = Infinity; + let selectedInfil = ""; + + playerOnlySpawns.forEach(({ Infiltration, Position: { x, y, z } }) => { + const dist = getDistance(X, Y, Z, x, y, z); + if (!!Infiltration && dist < closest) { + closest = dist; + selectedInfil = Infiltration; + } + }); + + return selectedInfil; + }; + + const playerSpawns = PlayerSpawns[map].map((coords: Ixyz, index) => ({ + BotZoneName: "", + Categories: ["Player"], + ColliderParams: { + _parent: "SpawnSphereParams", + _props: { + Center: { + x: 0, + y: 0, + z: 0, + }, + Radius: 1, + }, + }, + isCustom: true, + CorePointId: 0, + DelayToCanSpawnSec: 4, + Id: uuidv4(), + Infiltration: getClosestInfil(coords.x, coords.y, coords.z), + Position: coords, + Rotation: random360(), + Sides: ["Pmc"], + })); + + // TODO: Check infils + // console.log(map); + // console.log(playerOnlySpawns[0], playerSpawns[0]); + + return [...playerOnlySpawns, ...playerSpawns]; +}; + +export const getClosestZone = ( + params: ISpawnPointParam[], + x: number, + y: number, + z: number +) => { + if ( + Array.isArray(params) && + !params.filter(({ BotZoneName }) => BotZoneName).length + ) + return ""; + + return ( + getSortedSpawnPointList(params, x, y, z).find( + ({ BotZoneName }) => !!BotZoneName + )?.BotZoneName || "" + ); +}; + +export const removeClosestSpawnsFromCustomBots = ( + CustomBots: Record, + SpawnPointParams: ISpawnPointParam[], + map: string, + mapConfigMap: string +) => { + if (!CustomBots[map] || !CustomBots[map].length) { + console.log(map, "Is empty"); + return; + } + + const coords: Ixyz[] = CustomBots[map]; + + const { mapCullingNearPointValuePlayer, + mapCullingNearPointValuePmc, + mapCullingNearPointValueScav } = (mapConfig[mapConfigMap] as MapConfigType) + + const mapCullingNearPointValue = (mapCullingNearPointValuePlayer + + mapCullingNearPointValuePmc + + mapCullingNearPointValueScav) / 3 + + + let filteredCoords = coords.filter( + ({ x: X, y: Y, z: Z }) => + !SpawnPointParams.some(({ Position: { z, x, y } }) => { + return mapCullingNearPointValue > getDistance(X, Y, Z, x, y, z); + }) + ); + + const okayList = new Set(); + + filteredCoords = [...coords].filter(({ x: X, y: Y, z: Z }, index) => { + const result = !coords.some(({ z, x, y }) => { + const dist = getDistance(X, Y, Z, x, y, z); + return ( + mapCullingNearPointValue * 1.3 > dist && + dist !== 0 && + !okayList.has("" + x + y + z) + ); + }); + if (!result) okayList.add("" + X + Y + Z); + return result; + }); + + console.log( + map, + coords.length, + ">", + filteredCoords.length, + "culled", + coords.length - filteredCoords.length, + "spawns" + ); + return filteredCoords; +}; diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts b/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts new file mode 100644 index 0000000..2a0accd --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/updateSpawnLocations.ts @@ -0,0 +1,71 @@ +import { ILocation } from "@spt/models/eft/common/ILocation"; +import { configLocations } from "./constants"; +import _config from "../../config/config.json"; +import { getRandomInArray, shuffle } from "./utils"; +import advancedConfig from "../../config/advancedConfig.json"; +import { ISpawnPointParam } from "@spt/models/eft/common/ILocationBase"; +import { globalValues } from "../GlobalValues"; +import getSortedSpawnPointList, { + getClosestZone, + getDistance, + uuidv4, +} from "./spawnZoneUtils"; + +export default function updateSpawnLocations( + locationList: ILocation[], + config: typeof _config +) { + for (let index = 0; index < locationList.length; index++) { + const map = configLocations[index]; + const mapSpawns = [...globalValues.indexedMapSpawns[index]]; + + const playerSpawns = mapSpawns.filter( + (point) => point?.["type"] === "player" + ); + + const playerSpawn: ISpawnPointParam = getRandomInArray(playerSpawns); + globalValues.playerSpawn = playerSpawn; + + const { x, y, z } = playerSpawn.Position; + + const sortedSpawnPointList = getSortedSpawnPointList(mapSpawns, x, y, z); + + const possibleSpawnList: ISpawnPointParam[] = []; + + sortedSpawnPointList.forEach((point) => { + if ( + possibleSpawnList.length < advancedConfig.SpawnpointAreaTarget && + point?.["type"] === "player" + ) { + point.ColliderParams._props.Radius = 1 + possibleSpawnList.push(point); + } + }); + + // const possibleSpawnListSet = new Set(possibleSpawnList.map(({ Id }) => Id)); + + locationList[index].base.SpawnPointParams = [ + ...possibleSpawnList, + ...sortedSpawnPointList.filter((point) => point["type"] !== "player"), + ]; + + // { + // if (point["type"] === "player" && !possibleSpawnListSet.has(point.Id)) { + // point.Categories = []; + // point.Sides = []; + // } + + // return point; + // } + + // console.log( + // map, + // locationList[index].base.SpawnPointParams.filter( + // (point) => point?.["type"] === "player" + // ).length, + // locationList[index].base.SpawnPointParams.filter( + // (point) => point?.Categories[0] === "Player" + // ).length + // ); + } +} diff --git a/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts b/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts new file mode 100644 index 0000000..3e6657e --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/Spawning/utils.ts @@ -0,0 +1,538 @@ +import { + IBossLocationSpawn, + IWave, + WildSpawnType, +} from "@spt/models/eft/common/ILocationBase"; +import _config from "../../config/config.json"; +import mapConfig from "../../config/mapConfig.json"; +import { ILocation } from "@spt/models/eft/common/ILocation"; +import { configLocations, 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 + 60; + + 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; + let slotMin = (Math.round(Math.random() * slotMax) || 1) - 1; + + if (wildSpawnType === "marksman" && slotMin < 1) slotMin = 1; + waves.push({ + BotPreset, + BotSide: getBotSide(wildSpawnType), + SpawnPoints: getZone( + specialZones, + combinedZones, + waves.length >= totalWaves + ), + isPlayers: isPlayer, + slots_max: slotMax, + slots_min: slotMin, + time_min: min, + time_max: 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 buildBotWaves = ( + botTotal: number, + escapeTimeLimit: number, + maxGroup: number, + groupChance: number, + bossZones: string[], + difficulty: number, + botType: string, + ForceSpawn: boolean, + botDistribution: number, + spawnDelay = 0 +): IBossLocationSpawn[] => { + if (!botTotal) return []; + const pushToEnd = botDistribution > 1; + const pullFromEnd = botDistribution < 1; + const botToZoneTotal = bossZones.length / botTotal; + const isMarksman = botType === "marksman"; + const isPMC = botType === "pmcUSEC" || botType === "pmcBEAR"; + + let startTime = pushToEnd + ? Math.round((botDistribution - 1) * escapeTimeLimit) + : spawnDelay; + + escapeTimeLimit = pullFromEnd + ? Math.round(escapeTimeLimit * botDistribution) + : Math.round(escapeTimeLimit - startTime); + + const averageTime = Math.round(escapeTimeLimit / botTotal); + + const waves: IBossLocationSpawn[] = []; + let maxSlotsReached = botTotal; + if (maxGroup < 1) maxGroup = 1; + while (botTotal > 0) { + const allowGroup = groupChance > Math.random(); + let bossEscortAmount = allowGroup + ? Math.round(maxGroup * Math.random()) + : 0; + + if ( + bossEscortAmount < 0 || + (bossEscortAmount > 0 && bossEscortAmount + 1 > maxSlotsReached) + ) { + bossEscortAmount = 0; + } + + const totalCountThisWave = isMarksman ? 1 : bossEscortAmount + 1; + const totalCountThusFar = botTotal - maxSlotsReached; + + const BossDifficult = getDifficulty(difficulty); + + waves.push({ + BossChance: 100, + BossDifficult, + BossEscortAmount: bossEscortAmount.toString(), + BossEscortDifficult: BossDifficult, + BossEscortType: botType, + BossName: botType, + BossPlayer: false, + BossZone: bossZones[Math.floor(totalCountThusFar * botToZoneTotal)] || "", + ForceSpawn, + IgnoreMaxBots: ForceSpawn, + RandomTimeSpawn: false, + Time: startTime, + Supports: null, + TriggerId: "", + TriggerName: "", + SpawnMode: isPMC ? ["pve"] : ["regular", "pve"], + }); + + startTime += Math.round(totalCountThisWave * averageTime); + + maxSlotsReached -= 1 + (isMarksman ? 0 : bossEscortAmount); + if (maxSlotsReached <= 0) break; + } + // isMarksman && + // console.log( + // // bossZones, + // botType, + // bossZones.length, + // waves.map(({ Time, BossZone }) => ({ Time, BossZone })) + // ); + return waves; +}; + +export const buildZombie = ( + botTotal: number, + escapeTimeLimit: number, + botDistribution: number, + BossChance: number = 100 +): IBossLocationSpawn[] => { + if (!botTotal) return []; + const pushToEnd = botDistribution > 1; + const pullFromEnd = botDistribution < 1; + + let startTime = pushToEnd + ? Math.round((botDistribution - 1) * escapeTimeLimit) + : 0; + + escapeTimeLimit = pullFromEnd + ? Math.round(escapeTimeLimit * botDistribution) + : Math.round(escapeTimeLimit - startTime); + + const averageTime = Math.round(escapeTimeLimit / botTotal); + + const waves: IBossLocationSpawn[] = []; + let maxSlotsReached = botTotal; + + while (botTotal > 0) { + const allowGroup = 0.2 > Math.random(); + let bossEscortAmount = allowGroup ? Math.round(4 * Math.random()) : 0; + + if (bossEscortAmount < 0) bossEscortAmount = 0; + + const totalCountThisWave = bossEscortAmount + 1; + + const main = getRandomZombieType(); + waves.push({ + BossChance, + BossDifficult: "normal", + BossEscortAmount: "0", + BossEscortDifficult: "normal", + BossEscortType: main, + BossName: main, + BossPlayer: false, + BossZone: "", + Delay: 0, + IgnoreMaxBots: false, + RandomTimeSpawn: false, + Time: startTime, + Supports: new Array(bossEscortAmount).fill("").map(() => ({ + BossEscortType: getRandomZombieType(), + BossEscortDifficult: ["normal"], + BossEscortAmount: "1", + })), + TriggerId: "", + TriggerName: "", + SpawnMode: ["regular", "pve"], + }); + + startTime += Math.round(totalCountThisWave * averageTime); + + maxSlotsReached -= 1 + bossEscortAmount; + if (maxSlotsReached <= 0) break; + } + // console.log(waves) + return waves; +}; + +export interface MapSettings { + sniperQuantity?: number; + initialSpawnDelay: number; + smoothingDistribution: number; + mapCullingNearPointValuePlayer: number; + mapCullingNearPointValuePmc: number; + mapCullingNearPointValueScav: number; + spawnMinDistance: number; + 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; + mapCullingNearPointValuePlayer: number; + mapCullingNearPointValuePmc: number; + mapCullingNearPointValueScav: 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; + } + } +}; + +export const getRandomInArray = (arr: T[]): T => + arr[Math.floor(Math.random() * arr.length)]; + +export const enforceSmoothing = (locationList: ILocation[]) => { + for (let index = 0; index < locationList.length; index++) { + const waves = locationList[index].base.BossLocationSpawn; + + const Bosses: IBossLocationSpawn[] = []; + let notBosses: IBossLocationSpawn[] = []; + + const notBossesSet = new Set([ + "infectedLaborant", + "infectedAssault", + "infectedCivil", + WildSpawnType.ASSAULT, + WildSpawnType.MARKSMAN, + "pmcBEAR", + "pmcUSEC", + ]); + + for (const wave of waves) { + if (notBossesSet.has(wave.BossName)) { + notBosses.push(wave); + } else { + Bosses.push(wave); + } + } + + let first = Infinity, + last = -Infinity; + + notBosses.forEach((notBoss) => { + first = Math.min(notBoss.Time, first); + last = Math.max(notBoss.Time, last); + }); + + if (first < 15) first = 15; + + notBosses = notBosses.sort((a, b) => a.Time - b.Time); + + // console.log(notBosses.map(({ Time }) => Time)) + + let start = first; + const smoothingDistribution = (mapConfig[configLocations[index]] as any) + .smoothingDistribution as number; + + const increment = + (Math.round((last - first) / notBosses.length) * 2) * smoothingDistribution; + + for (let index = 0; index < notBosses.length; index++) { + const ratio = (index + 1) / notBosses.length; + // console.log(ratio); + notBosses[index].Time = start; + let inc = Math.round(increment * ratio); + if (inc < 10) inc = 5; + start += inc; + } + + // console.log( + // configLocations[index], + // last, + // notBosses.map(({ Time, BossName }) => ({ BossName, Time })) + // ); + + locationList[index].base.BossLocationSpawn = [...Bosses, ...notBosses]; + } +}; + +export const looselyShuffle = (arr: T[], shuffleStep: number = 3): T[] => { + const n = arr.length; + const halfN = Math.floor(n / 2); + for (let i = shuffleStep - 1; i < halfN; i += shuffleStep) { + // Pick a random index from the second half of the array to swap with the current index + const randomIndex = halfN + Math.floor(Math.random() * (n - halfN)); + // Swap the elements at the current index and the random index + const temp = arr[i]; + arr[i] = arr[randomIndex]; + arr[randomIndex] = temp; + } + + return arr; +}; diff --git a/user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts b/user/mods/DewardianDev-MOAR/src/Tests/checkPresets.ts new file mode 100644 index 0000000..57b753d --- /dev/null +++ b/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/user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts b/user/mods/DewardianDev-MOAR/src/Zombies/Zombies.ts new file mode 100644 index 0000000..1125559 --- /dev/null +++ b/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/user/mods/DewardianDev-MOAR/src/mod.ts b/user/mods/DewardianDev-MOAR/src/mod.ts new file mode 100644 index 0000000..73e72f4 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/mod.ts @@ -0,0 +1,41 @@ +import { DependencyContainer } from "tsyringe"; +import { IPostSptLoadMod } from "@spt/models/external/IPostSptLoadMod"; +import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod"; +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"; +import { setupSpawns } from "./SpawnZoneChanges/setupSpawn"; + +class Moar implements IPostSptLoadMod, IPreSptLoadMod, IPostDBLoadMod { + preSptLoad(container: DependencyContainer): void { + if (enableBotSpawning) { + setupRoutes(container); + } + } + + postDBLoad(container: DependencyContainer): void { + if (enableBotSpawning) { + setupSpawns(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/user/mods/DewardianDev-MOAR/src/utils.ts b/user/mods/DewardianDev-MOAR/src/utils.ts new file mode 100644 index 0000000..a72f393 --- /dev/null +++ b/user/mods/DewardianDev-MOAR/src/utils.ts @@ -0,0 +1,56 @@ +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(" ");