diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json index bad725239..2218ec0b8 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json @@ -296,6 +296,20 @@ "minLimitPercent": 15 } }, + "ravangezryachiyevent": { + "armor": { + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + }, + "weapon": { + "highestMax": 100, + "lowestMax": 80, + "maxDelta": 40, + "minDelta": 20, + "minLimitPercent": 15 + } + }, "sectantpriest": { "armor": { "maxDelta": 10, @@ -324,6 +338,48 @@ "minLimitPercent": 15 } }, + "sectantoni": { + "armor": { + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + }, + "weapon": { + "highestMax": 100, + "lowestMax": 90, + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + } + }, + "sectantpredvestnik": { + "armor": { + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + }, + "weapon": { + "highestMax": 100, + "lowestMax": 90, + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + } + }, + "sectantprizrak": { + "armor": { + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + }, + "weapon": { + "highestMax": 100, + "lowestMax": 90, + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + } + }, "shooterbtr": { "armor": { "maxDelta": 0, @@ -2758,7 +2814,7 @@ "bossPartisan": 5, "bossSanitar": 5, "bossTagilla": 5, - "bossTagillaAgro": 5, + "bossTagillAagro": 5, "bossTest": 10, "bossZryachiy": 5, "crazyAssaultEvent": 15, diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/item.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/item.json index fb754f393..f6de617c6 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/item.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/item.json @@ -592,7 +592,8 @@ "64d4b23dc1b37504b41ac2b6", "678f84bb9e85556ca60f0362", "67ab3d4b83869afd170fdd3f", - "678fa929819ddc4c350c0317" + "678fa929819ddc4c350c0317", + "5af056f186f7746da511291f" ], "rewardItemTypeBlacklist": [ "65649eb40bf0ed77b8044453" diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json index 41b5a386f..ddd6e5885 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json @@ -172,7 +172,7 @@ }, "66e3e2ee2136472d220bcb36": { "name": "Night of The Cult", - "season": "None", + "season": "Halloween", "startTimestamp": 1341615600000, "endTimestamp": "", "yearly": false diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json index 4f92c947d..206ac93ee 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json @@ -37,7 +37,8 @@ "65702610cfc010a0f5006a41", "5737250c2459776125652acc", "657023a9126cc4a57d0e17a6", - "5c1127d0d174af29be75cf68" + "5c1127d0d174af29be75cf68", + "5af056f186f7746da511291f" ], "customItemCategoryList": [], "damagedAmmoPacks": true, @@ -346,6 +347,7 @@ "useTraderPriceForOffersIfHigher": true, "generateBaseFleaPrices": { "useHandbookPrice": true, + "generatePresetPriceByChildren": true, "priceMultiplier": 1.5, "itemTplMultiplierOverride": { "5780cf7f2459777de4559322": 1.8 diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 5d75f300f..19955484f 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -457,8 +457,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -481,8 +481,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -505,8 +505,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -529,8 +529,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -553,8 +553,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -577,8 +577,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -601,8 +601,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -625,8 +625,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -649,8 +649,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -695,8 +695,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -741,8 +741,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -780,8 +780,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -819,8 +819,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -858,8 +858,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -897,8 +897,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -936,8 +936,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -975,8 +975,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -1224,7 +1224,7 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [], @@ -2495,8 +2495,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2517,8 +2517,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2539,8 +2539,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2561,8 +2561,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2583,8 +2583,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2605,8 +2605,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2627,8 +2627,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2649,8 +2649,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2671,8 +2671,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2715,8 +2715,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2759,8 +2759,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2796,8 +2796,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2833,8 +2833,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2870,8 +2870,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2907,8 +2907,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2944,8 +2944,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -2981,8 +2981,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -3238,7 +3238,7 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -4003,8 +4003,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4027,8 +4027,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4051,8 +4051,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4075,8 +4075,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4099,8 +4099,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4123,8 +4123,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4147,8 +4147,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4171,8 +4171,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4195,8 +4195,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4241,8 +4241,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4287,8 +4287,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4326,8 +4326,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4365,8 +4365,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4404,8 +4404,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4443,8 +4443,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4482,8 +4482,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4521,8 +4521,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4800,7 +4800,7 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -4824,8 +4824,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4846,8 +4846,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4868,8 +4868,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4890,8 +4890,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4912,8 +4912,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4934,8 +4934,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4956,8 +4956,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -4978,8 +4978,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5000,8 +5000,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5044,8 +5044,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5088,8 +5088,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5125,8 +5125,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5162,8 +5162,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5199,8 +5199,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5236,8 +5236,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5273,8 +5273,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5310,8 +5310,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -5567,7 +5567,7 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -6995,8 +6995,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7019,8 +7019,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7043,8 +7043,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7067,8 +7067,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7091,8 +7091,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7115,8 +7115,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "pve", @@ -7139,8 +7139,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7163,8 +7163,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7187,8 +7187,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7233,8 +7233,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7279,8 +7279,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7318,8 +7318,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7357,8 +7357,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7396,8 +7396,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7435,8 +7435,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7474,8 +7474,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7513,8 +7513,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7792,7 +7792,7 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -7816,8 +7816,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7838,8 +7838,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7860,8 +7860,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7882,8 +7882,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7904,8 +7904,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7926,8 +7926,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7948,8 +7948,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7970,8 +7970,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -7992,8 +7992,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8036,8 +8036,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8080,8 +8080,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8117,8 +8117,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8154,8 +8154,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8191,8 +8191,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8228,8 +8228,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8265,8 +8265,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8302,8 +8302,8 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8559,7 +8559,7 @@ "BossPlayer": false, "BossZone": "", "Delay": 0, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -8585,8 +8585,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8609,8 +8609,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8633,8 +8633,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8657,8 +8657,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8681,8 +8681,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8705,8 +8705,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8729,8 +8729,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8753,8 +8753,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8777,8 +8777,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8823,8 +8823,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8869,8 +8869,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8908,8 +8908,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8947,8 +8947,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -8986,8 +8986,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -9025,8 +9025,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -9064,8 +9064,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -9103,8 +9103,8 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, - "IgnoreMaxBots": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ "regular", @@ -9382,7 +9382,7 @@ "Delay": 0, "DependKarma": false, "DependKarmaPVE": false, - "ForceSpawn": false, + "ForceSpawn": true, "IgnoreMaxBots": true, "RandomTimeSpawn": false, "SpawnMode": [ @@ -9395,28 +9395,159 @@ "TriggerName": "" } ] - } - }, - "eventBotMapping": { - "arenaFighterEvent": "arenaFighterEvent", - "crazyAssaultEvent": "crazyAssaultEvent", - "peacefullZryachiyEvent": "bossZryachiy", - "ravangeZryachiyEvent": "ravangeZryachiyEvent", - "sectactPriestEvent": "sectantPriest" - }, - "eventGear": { - "christmas": { - "bear": { - "FaceCover": { - "5c1a1e3f2e221602b66cc4c2": 30, - "675ac888803644528007b3f6": 30 - }, - "Headwear": { - "5a43943586f77416ad2f06e2": 80, - "5a43957686f7742a2c2f11b0": 80 - } - }, - "bossboar": { + }, + "halloweennightcult": { + "shoreline": [ + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "sectantPredvestnik", + "BossPlayer": false, + "BossZone": "ZoneMeteoStation", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "ShowOnTarkovMap": false, + "ShowOnTarkovMapPvE": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": [ + { + "BossEscortType": "sectantOni", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + }, + { + "BossEscortType": "sectantPrizrak", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ], + "woods": [ + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "sectantPredvestnik", + "BossPlayer": false, + "BossZone": "ZoneScavBase2", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": true, + "ShowOnTarkovMap": false, + "ShowOnTarkovMapPvE": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [ + { + "BossEscortType": "sectantOni", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + }, + { + "BossEscortType": "sectantPrizrak", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ], + "bigmap": [ + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "sectantPredvestnik", + "BossPlayer": false, + "BossZone": "ZoneScavBase", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": true, + "ShowOnTarkovMap": false, + "ShowOnTarkovMapPvE": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [ + { + "BossEscortType": "sectantOni", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + }, + { + "BossEscortType": "sectantPrizrak", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortAmount": "1" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ] + } + }, + "eventBotMapping": { + "arenaFighterEvent": "arenaFighterEvent", + "crazyAssaultEvent": "crazyAssaultEvent", + "peacefullZryachiyEvent": "bossZryachiy", + "ravangeZryachiyEvent": "ravangeZryachiyEvent", + "sectactPriestEvent": "sectantPriest" + }, + "eventGear": { + "christmas": { + "bear": { + "FaceCover": { + "5c1a1e3f2e221602b66cc4c2": 30, + "675ac888803644528007b3f6": 30 + }, + "Headwear": { + "5a43943586f77416ad2f06e2": 80, + "5a43957686f7742a2c2f11b0": 80 + } + }, + "bossboar": { "FaceCover": { "5c1a1e3f2e221602b66cc4c2": 1 }, @@ -9688,16 +9819,16 @@ "disableWaves": [], "enabled": true, "mapInfectionAmount": { - "Sandbox": 5, - "factory4": 5, + "Sandbox": 25, + "factory4": 50, "laboratory": 100, - "Woods": 5, - "bigmap": 5, - "Shoreline": 5, - "Interchange": 5, - "RezervBase": 5, - "Lighthouse": 5, - "TarkovStreets": 5 + "Woods": 60, + "bigmap": 0, + "Shoreline": 50, + "Interchange": 0, + "RezervBase": 50, + "Lighthouse": 50, + "TarkovStreets": 50 } } }, @@ -9948,9 +10079,11 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "pmcBEAR", + "pmcUSEC" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "assault", "Neutral": [ "marksman", @@ -10002,12 +10135,10 @@ "spiritWinter", "spiritSpring", "peacemaker", - "pmcBEAR", - "pmcUSEC", "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10017,7 +10148,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossTest", "Neutral": [ "marksman", @@ -10074,7 +10205,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10084,7 +10215,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBully", "Neutral": [ "marksman", @@ -10141,7 +10272,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10218,7 +10349,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBully", "Neutral": [ "marksman", @@ -10275,7 +10406,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10285,7 +10416,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKilla", "Neutral": [ "marksman", @@ -10342,7 +10473,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10352,7 +10483,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKojaniy", "Neutral": [ "marksman", @@ -10409,7 +10540,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10419,7 +10550,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKojaniy", "Neutral": [ "marksman", @@ -10476,7 +10607,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10484,9 +10615,11 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "pmcBEAR", + "pmcUSEC" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "pmcBot", "Neutral": [ "marksman", @@ -10538,12 +10671,10 @@ "spiritWinter", "spiritSpring", "peacemaker", - "pmcBEAR", - "pmcUSEC", "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10620,7 +10751,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossGluhar", "Neutral": [ "marksman", @@ -10677,7 +10808,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10687,7 +10818,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharAssault", "Neutral": [ "marksman", @@ -10744,7 +10875,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10754,7 +10885,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharSecurity", "Neutral": [ "marksman", @@ -10811,7 +10942,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10821,7 +10952,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharScout", "Neutral": [ "marksman", @@ -10878,7 +11009,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10888,7 +11019,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharSnipe", "Neutral": [ "marksman", @@ -10945,7 +11076,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10955,7 +11086,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerSanitar", "Neutral": [ "marksman", @@ -11012,7 +11143,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11022,7 +11153,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossSanitar", "Neutral": [ "marksman", @@ -11079,7 +11210,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11357,7 +11488,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossTagilla", "Neutral": [ "marksman", @@ -11414,7 +11545,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11424,7 +11555,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerTagilla", "Neutral": [ "marksman", @@ -11481,7 +11612,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11625,7 +11756,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKnight", "Neutral": [ "marksman", @@ -11682,7 +11813,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11692,7 +11823,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBigPipe", "Neutral": [ "marksman", @@ -11749,7 +11880,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11759,7 +11890,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBirdEye", "Neutral": [ "marksman", @@ -11816,7 +11947,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11960,7 +12091,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBoar", "Neutral": [ "marksman", @@ -12017,7 +12148,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12027,7 +12158,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoar", "Neutral": [ "marksman", @@ -12084,7 +12215,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12228,7 +12359,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBoarSniper", "Neutral": [ "marksman", @@ -12285,7 +12416,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12563,7 +12694,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoarClose1", "Neutral": [ "marksman", @@ -12620,7 +12751,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12630,7 +12761,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoarClose2", "Neutral": [ "marksman", @@ -12687,7 +12818,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12697,7 +12828,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKolontay", "Neutral": [ "marksman", @@ -12754,7 +12885,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12764,7 +12895,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKolontayAssault", "Neutral": [ "marksman", @@ -12821,7 +12952,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12831,7 +12962,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKolontaySecurity", "Neutral": [ "marksman", @@ -12888,7 +13019,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12965,7 +13096,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossPartisan", "Neutral": [ "marksman", @@ -13022,7 +13153,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -13231,9 +13362,10 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "assault" ], - "BearEnemyChance": 15, + "BearEnemyChance": 90, "BearPlayerBehaviour": "ChancedEnemies", "BotRole": "pmcBEAR", "Neutral": [ @@ -13291,7 +13423,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecEnemyChance": 30, + "UsecEnemyChance": 90, "UsecPlayerBehaviour": "ChancedEnemies" }, { @@ -13300,12 +13432,7 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" - ], - "BearEnemyChance": 30, - "BearPlayerBehaviour": "ChancedEnemies", - "BotRole": "pmcUSEC", - "Neutral": [ + "infectedTagilla", "marksman", "assault", "bossTest", @@ -13315,7 +13442,6 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "cursedAssault", "bossGluhar", "followerGluharAssault", "followerGluharSecurity", @@ -13323,6 +13449,12 @@ "followerGluharSnipe", "followerSanitar", "bossSanitar", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13330,7 +13462,6 @@ "bossTagilla", "followerTagilla", "exUsec", - "gifter", "bossKnight", "followerBigPipe", "followerBirdEye", @@ -13342,25 +13473,24 @@ "arenaFighterEvent", "bossBoarSniper", "crazyAssaultEvent", - "peacefullZryachiyEvent", "sectactPriestEvent", "ravangeZryachiyEvent", "followerBoarClose1", "followerBoarClose2", "bossKolontay", "followerKolontayAssault", - "followerKolontaySecurity", - "shooterBTR", - "bossPartisan", - "spiritWinter", - "spiritSpring", - "peacemaker", - "pmcBEAR", - "pmcUSEC", - "skier" + "followerKolontaySecurity" + ], + "BearEnemyChance": 90, + "BearPlayerBehaviour": "ChancedEnemies", + "BotRole": "pmcUSEC", + "Neutral": [ + "gifter", + "peacefullZryachiyEvent", + "shooterBTR" ], "SavagePlayerBehaviour": "Neutral", - "UsecEnemyChance": 15, + "UsecEnemyChance": 90, "UsecPlayerBehaviour": "ChancedEnemies" }, { @@ -13369,11 +13499,7 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" - ], - "BearPlayerBehaviour": "Neutral", - "BotRole": "skier", - "Neutral": [ + "infectedTagilla", "marksman", "assault", "bossTest", @@ -13383,7 +13509,6 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "cursedAssault", "bossGluhar", "followerGluharAssault", "followerGluharSecurity", @@ -13391,6 +13516,12 @@ "followerGluharSnipe", "followerSanitar", "bossSanitar", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13398,7 +13529,6 @@ "bossTagilla", "followerTagilla", "exUsec", - "gifter", "bossKnight", "followerBigPipe", "followerBirdEye", @@ -13410,22 +13540,20 @@ "arenaFighterEvent", "bossBoarSniper", "crazyAssaultEvent", - "peacefullZryachiyEvent", "sectactPriestEvent", "ravangeZryachiyEvent", "followerBoarClose1", "followerBoarClose2", "bossKolontay", "followerKolontayAssault", - "followerKolontaySecurity", - "shooterBTR", - "bossPartisan", - "spiritWinter", - "spiritSpring", - "peacemaker", - "pmcBEAR", - "pmcUSEC", - "skier" + "followerKolontaySecurity" + ], + "BearPlayerBehaviour": "Neutral", + "BotRole": "skier", + "Neutral": [ + "gifter", + "peacefullZryachiyEvent", + "shooterBTR" ], "SavagePlayerBehaviour": "Neutral", "UsecPlayerBehaviour": "Neutral" diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/bosstagillaagro.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/bosstagillaagro.json index 79dbb8b9f..5040e441f 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/bosstagillaagro.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/bosstagillaagro.json @@ -2124,7 +2124,9 @@ }, "experience": { "aggressorBonus": { - "normal": 0.05 + "normal": 0.05, + "easy": 0.05, + "hard": 0.05 }, "level": { "max": 1, @@ -2134,10 +2136,20 @@ "normal": { "max": 6666, "min": 6666 + }, + "easy": { + "max": 6666, + "min": 6666 + }, + "hard": { + "max": 6666, + "min": 6666 } }, "standingForKill": { - "normal": -0.2 + "normal": -0.2, + "easy": -0.2, + "hard": -0.2 }, "useSimpleAnimator": false }, diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantoni.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantoni.json index 686de70d1..87819e555 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantoni.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantoni.json @@ -26,13 +26,25 @@ "normal": { "min": 2100, "max": 2100 + }, + "easy": { + "min": 2100, + "max": 2100 + }, + "hard": { + "min": 2100, + "max": 2100 } }, "standingForKill": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "aggressorBonus": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "useSimpleAnimator": false }, diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantpredvestnik.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantpredvestnik.json index a9833fc59..ff17aef67 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantpredvestnik.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantpredvestnik.json @@ -25,13 +25,25 @@ "normal": { "min": 2300, "max": 2300 + }, + "easy": { + "min": 2300, + "max": 2300 + }, + "hard": { + "min": 2300, + "max": 2300 } }, "standingForKill": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "aggressorBonus": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "useSimpleAnimator": false }, diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantprizrak.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantprizrak.json index 1e59a44df..941f152c4 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantprizrak.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/sectantprizrak.json @@ -26,13 +26,25 @@ "normal": { "min": 2100, "max": 2100 + }, + "easy": { + "min": 2100, + "max": 2100 + }, + "hard": { + "min": 2100, + "max": 2100 } }, "standingForKill": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "aggressorBonus": { - "normal": 0 + "normal": 0, + "easy": 0, + "hard": 0 }, "useSimpleAnimator": false }, diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/bigmap/base.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/bigmap/base.json index dc9992185..76760a6fb 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/bigmap/base.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/bigmap/base.json @@ -138,7 +138,7 @@ "TriggerName": "" }, { - "BossChance": 18, + "BossChance": 20, "BossDifficult": "normal", "BossEscortAmount": "2", "BossEscortDifficult": "normal", diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/lighthouse/base.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/lighthouse/base.json index fbf282930..c927550a8 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/lighthouse/base.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/lighthouse/base.json @@ -84,7 +84,7 @@ "TriggerName": "" }, { - "BossChance": 50, + "BossChance": 20, "BossDifficult": "normal", "BossEscortAmount": "2", "BossEscortDifficult": "normal", diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/shoreline/base.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/shoreline/base.json index 9eb1042ed..c09d7ac80 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/shoreline/base.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/locations/shoreline/base.json @@ -110,7 +110,7 @@ "TriggerName": "" }, { - "BossChance": 25, + "BossChance": 20, "BossDifficult": "normal", "BossEscortAmount": "0", "BossEscortDifficult": "normal", diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs index cc8e7a8c4..ee990938d 100644 --- a/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs +++ b/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs @@ -1,5 +1,6 @@ using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Controllers; +using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Launcher; using SPTarkov.Server.Core.Models.Spt.Launcher; using SPTarkov.Server.Core.Utils; @@ -71,10 +72,19 @@ public ValueTask Profiles() ); } - public ValueTask Profile(LoginRequestData sessionId) + public ValueTask Profile(LoginRequestData username) { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2ProfileResponse { Response = launcherV2Controller.GetMiniProfileFromUsername(sessionId) }) + httpResponseUtil.NoBody(new LauncherV2ProfileResponse { Response = launcherV2Controller.GetMiniProfileFromUsername(username) }) + ); + } + + public ValueTask Wipe(RegisterData info) + { + return new ValueTask( + httpResponseUtil.NoBody( + new LauncherV2WipeResponse { Response = launcherV2Controller.Wipe(info), Profiles = profileController.GetMiniProfiles() } + ) ); } } diff --git a/Libraries/SPTarkov.Server.Core/Controllers/BuildController.cs b/Libraries/SPTarkov.Server.Core/Controllers/BuildController.cs index d5474e875..0aad00236 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/BuildController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/BuildController.cs @@ -82,11 +82,11 @@ ICloner cloner /// public void SaveWeaponBuild(MongoId sessionId, PresetBuildActionRequestData request) { - var pmcData = profileHelper.GetPmcProfile(sessionId); + var profile = profileHelper.GetFullProfile(sessionId); // Replace duplicate Id's. The first item is the base item. // The root ID and the base item ID need to match. - request.Items = itemHelper.ReplaceIDs(request.Items, pmcData); + request.Items = itemHelper.ReplaceIDs(request.Items, profile.CharacterData.PmcData); request.Root = request.Items.FirstOrDefault().Id; // Create new object ready to save into profile userbuilds.weaponBuilds @@ -98,15 +98,13 @@ public void SaveWeaponBuild(MongoId sessionId, PresetBuildActionRequestData requ Items = request.Items.ToList(), }; - var profile = profileHelper.GetFullProfile(sessionId); - var savedWeaponBuilds = profile.UserBuildData.WeaponBuilds; - var existingBuild = savedWeaponBuilds.FirstOrDefault(x => x.Id == request.Id); + var existingBuild = savedWeaponBuilds.FirstOrDefault(build => build.Name == request.Name || build.Id == request.Id); if (existingBuild is not null) { // exists, replace profile.UserBuildData.WeaponBuilds.Remove(existingBuild); - profile.UserBuildData.WeaponBuilds.Add(existingBuild); + profile.UserBuildData.WeaponBuilds.Add(newBuild); } else { @@ -123,13 +121,12 @@ public void SaveWeaponBuild(MongoId sessionId, PresetBuildActionRequestData requ public void SaveEquipmentBuild(MongoId sessionID, PresetBuildActionRequestData request) { var profile = profileHelper.GetFullProfile(sessionID); - var pmcData = profile.CharacterData.PmcData; var existingSavedEquipmentBuilds = saveServer.GetProfile(sessionID).UserBuildData.EquipmentBuilds; // Replace duplicate ID's. The first item is the base item. // Root ID and the base item ID need to match. - request.Items = itemHelper.ReplaceIDs(request.Items, pmcData); + request.Items = itemHelper.ReplaceIDs(request.Items, profile.CharacterData.PmcData); var newBuild = new EquipmentBuild { diff --git a/Libraries/SPTarkov.Server.Core/Controllers/LauncherController.cs b/Libraries/SPTarkov.Server.Core/Controllers/LauncherController.cs index 5b801b213..69dffde9e 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/LauncherController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/LauncherController.cs @@ -8,6 +8,7 @@ using SPTarkov.Server.Core.Models.Spt.Mod; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; +using SPTarkov.Server.Core.Services.Mod; using SPTarkov.Server.Core.Utils; using Info = SPTarkov.Server.Core.Models.Eft.Profile.Info; @@ -22,6 +23,7 @@ public class LauncherController( ProfileHelper profileHelper, DatabaseService databaseService, ServerLocalisationService serverLocalisationService, + ProfileDataService profileDataService, ConfigServer configServer ) { @@ -168,6 +170,9 @@ public MongoId Wipe(RegisterData info) var profileInfo = saveServer.GetProfile(sessionId).ProfileInfo; profileInfo!.Edition = info.Edition; profileInfo.IsWiped = true; + + // Clear any data modders may have stored + profileDataService.ClearProfileData(sessionId); } return sessionId; diff --git a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs index 323abf49b..c9274ee59 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs @@ -173,4 +173,27 @@ public SptProfile GetProfile(MongoId sessionId) { return profileController.GetMiniProfile(GetSessionId(info)); } + + public bool Wipe(RegisterData info) + { + if (!CoreConfig.AllowProfileWipe) + { + return false; + } + + var sessionId = Login(info); + + if (!sessionId) + { + var profileInfo = saveServer + .GetProfiles() + .FirstOrDefault(x => x.Value.ProfileInfo?.Username == info.Username) + .Value.ProfileInfo; + + profileInfo!.Edition = info.Edition; + profileInfo.IsWiped = true; + } + + return sessionId; + } } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs index a83fe6728..05922c47b 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotGenerator.cs @@ -70,6 +70,12 @@ public PmcData GeneratePlayerScav(MongoId sessionId, string role, string difficu bot = GenerateBot(sessionId, bot, botTemplate, botGenDetails); + // Pscavs in live have same limb hp as their pmc character + if (profile?.Health?.BodyParts is not null) + { + CopyLimbHpValuesToBot(bot, profile.Health.BodyParts); + } + // Sets the name after scav name shown in parentheses bot.Info.MainProfileNickname = profile.Info.Nickname; @@ -104,10 +110,19 @@ public PmcData GeneratePlayerScav(MongoId sessionId, string role, string difficu WishList = bot.WishList, MoneyTransferLimitData = bot.MoneyTransferLimitData, IsPmc = bot.IsPmc, - Prestige = new Dictionary(), + Prestige = [], }; } + protected void CopyLimbHpValuesToBot(BotBase bot, Dictionary bodyParts) + { + foreach (var (partName, partProperties) in bodyParts) + { + bot.Health.BodyParts[partName].Health.Maximum = partProperties.Health.Maximum; + bot.Health.BodyParts[partName].Health.Current = bot.Health.BodyParts[partName].Health.Maximum; + } + } + /// /// Create 1 bot of the type/side/difficulty defined in botGenerationDetails /// diff --git a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs index 245790b45..2d2d95cef 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs @@ -473,7 +473,7 @@ OfferCreator offerCreator // Not barter or pack offer // Apply randomised properties RandomiseOfferItemUpdProperties(sellerId, itemWithChildren, itemToSellDetails, offerCreator); - barterScheme = CreateCurrencyBarterScheme(itemWithChildren, isPackOffer); + barterScheme = CreateCurrencyBarterScheme(itemWithChildren, false); } var createOfferDetails = new CreateFleaOfferDetails @@ -487,6 +487,7 @@ OfferCreator offerCreator Creator = offerCreator, SellInOnePiece = isPackOffer, // sellAsOnePiece - pack offer }; + CreateAndAddFleaOffer(createOfferDetails); } diff --git a/Libraries/SPTarkov.Server.Core/Helpers/DialogueHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/DialogueHelper.cs index 39e9a4423..377038088 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/DialogueHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/DialogueHelper.cs @@ -1,4 +1,5 @@ using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Profile; @@ -67,9 +68,10 @@ public MessagePreview GetMessagePreview(Models.Eft.Profile.Dialogue? dialogue) message.Items ??= new MessageItems(); message.Items.Data ??= []; - // Check reward count when item being moved isn't in reward list + // Check reward count when item being moved (and its children) isn't in reward list // If count is 0, it means after this move occurs the reward array will be empty and all rewards collected - var remainingItems = message.Items.Data.Where(x => x.Id != itemId); + var itemWithChildren = message.Items.Data.GetItemWithChildren(itemId); + var remainingItems = message.Items.Data.Except(itemWithChildren); if (!remainingItems.Any()) { message.RewardCollected = true; diff --git a/Libraries/SPTarkov.Server.Core/Helpers/HealthHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/HealthHelper.cs index f14b4341b..8b31adb21 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/HealthHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/HealthHelper.cs @@ -3,7 +3,6 @@ using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; -using SPTarkov.Server.Core.Models.Eft.Health; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; @@ -158,18 +157,39 @@ protected void ModifyProfileHealthProperties( // Process each effect for each part foreach (var (key, effectDetails) in partProperties.Effects ?? []) { - // Null guard - matchingProfilePart.Effects ??= new Dictionary(); + // Have effects we need to add, init effect array + matchingProfilePart.Effects ??= []; - // Effect already exists on limb in server profile, skip + if ( + key.Equals("MildMusclePain", StringComparison.OrdinalIgnoreCase) + && matchingProfilePart.Effects.ContainsKey("SevereMusclePain") + ) + { + // Edge case - client is trying to add mild pain when server already has severe, don't allow this + continue; + } + + // Effect on limb already exists in server profile, handle differently if (matchingProfilePart.Effects.ContainsKey(key)) { - // Edge case - effect already exists at destination, but we don't want to overwrite details + matchingProfilePart.Effects.TryGetValue(key, out var matchingEffectOnServer); + + // Edge case - effect already exists at destination, but we don't want to overwrite details e.g. Exhaustion if (effectsToSkip is not null && effectsToSkip.Contains(key)) { matchingProfilePart.Effects[key] = null; } + // Effect time has decreased while in raid, persist this reduction into profile + if ( + effectDetails?.Time is not null + && matchingEffectOnServer?.Time is not null + && effectDetails.Time < matchingEffectOnServer.Time + ) + { + matchingEffectOnServer.Time = effectDetails.Time; + } + continue; } diff --git a/Libraries/SPTarkov.Server.Core/Migration/AbstractProfileMigration.cs b/Libraries/SPTarkov.Server.Core/Migration/AbstractProfileMigration.cs index aec18c8f2..345f13597 100644 --- a/Libraries/SPTarkov.Server.Core/Migration/AbstractProfileMigration.cs +++ b/Libraries/SPTarkov.Server.Core/Migration/AbstractProfileMigration.cs @@ -6,7 +6,7 @@ namespace SPTarkov.Server.Core.Migration; public abstract class AbstractProfileMigration : IProfileMigration { public virtual string MigrationName { get; } - public virtual IEnumerable PrerequisiteMigrations { get; } + public virtual IEnumerable PrerequisiteMigrations { get; } = []; public abstract string FromVersion { get; } public abstract string ToVersion { get; } diff --git a/Libraries/SPTarkov.Server.Core/Migration/Migrations/Fixes/InvalidPocketFix.cs b/Libraries/SPTarkov.Server.Core/Migration/Migrations/Fixes/InvalidPocketFix.cs new file mode 100644 index 000000000..7d600293b --- /dev/null +++ b/Libraries/SPTarkov.Server.Core/Migration/Migrations/Fixes/InvalidPocketFix.cs @@ -0,0 +1,279 @@ +using System.Text.Json.Nodes; +using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Models.Common; +using SPTarkov.Server.Core.Models.Enums; +using SPTarkov.Server.Core.Services; + +namespace SPTarkov.Server.Core.Migration.Migrations; + +[Injectable] +public class InvalidPocketFix(DatabaseService databaseService) : AbstractProfileMigration +{ + public const string DEFAULT_POCKETS = "627a4e6b255f7527fb05a0f6"; + public const string UNHEARD_POCKETS = "65e080be269cbd5c5005e529"; + + public override string FromVersion + { + get { return "~4.0"; } + } + + public override string ToVersion + { + get { return "~4.0"; } + } + + public override string MigrationName + { + get { return "InvalidPocketFix"; } + } + + private enum PocketStatus + { + Valid, + Missing, + Invalid, + } + + private PocketStatus GetPmcPocketStatus(JsonObject profile) + { + if (profile["characters"]?["pmc"]?["Inventory"]?["items"] is not JsonArray items) + { + // Uninitialized profile, just pass valid + return PocketStatus.Valid; + } + + foreach (var itemNode in items) + { + if (itemNode is not JsonObject itemObj) + { + continue; + } + + if ( + itemObj.TryGetPropertyValue("slotId", out var slotNode) + && slotNode is JsonValue slotValue + && slotValue.TryGetValue(out var slotId) + && slotId == "Pockets" + ) + { + if ( + itemObj.TryGetPropertyValue("_tpl", out var tplNode) + && tplNode is JsonValue tplValue + && tplValue.TryGetValue(out var template) + ) + { + return databaseService.GetItems().ContainsKey(template) ? PocketStatus.Valid : PocketStatus.Invalid; + } + } + } + + return PocketStatus.Missing; + } + + private PocketStatus GetScavPocketStatus(JsonObject profile) + { + if (profile["characters"]?["scav"]?["Inventory"]?["items"] is not JsonArray items) + { + // Uninitialized profile, just pass valid + return PocketStatus.Valid; + } + + foreach (var itemNode in items) + { + if (itemNode is not JsonObject itemObj) + { + continue; + } + + if ( + itemObj.TryGetPropertyValue("slotId", out var slotNode) + && slotNode is JsonValue slotValue + && slotValue.TryGetValue(out var slotId) + && slotId == "Pockets" + ) + { + if ( + itemObj.TryGetPropertyValue("_tpl", out var tplNode) + && tplNode is JsonValue tplValue + && tplValue.TryGetValue(out var template) + ) + { + return databaseService.GetItems().ContainsKey(template) ? PocketStatus.Valid : PocketStatus.Invalid; + } + } + } + + return PocketStatus.Missing; + } + + private bool HasCompletedOldPatterns(JsonObject profile) + { + if (profile["characters"]?["pmc"]?["Quests"] is not JsonArray quests) + { + return false; + } + + foreach (var questNode in quests) + { + if (questNode is not JsonObject questObj) + { + continue; + } + + if ( + questObj.TryGetPropertyValue("qid", out var qIdNode) + && qIdNode is JsonValue qIdValue + && qIdValue.TryGetValue(out var qId) + && qId == QuestTpl.OLD_PATTERNS.ToString() + && questObj.TryGetPropertyValue("status", out var statusNode) + && statusNode is JsonValue statusValue + && statusValue.TryGetValue(out var status) + && status.Equals(nameof(QuestStatusEnum.Success), StringComparison.OrdinalIgnoreCase) + ) + { + return true; + } + } + + return false; + } + + private bool IsUnheardProfile(JsonObject profile) + { + var gameVersion = profile?["characters"]?["pmc"]?["Info"]?["GameVersion"]?.GetValue(); + + if (!string.IsNullOrEmpty(gameVersion)) + { + return gameVersion.Equals("unheard_edition", StringComparison.OrdinalIgnoreCase); + } + + return false; + } + + private JsonObject CreatePocketItem(string parentId, string defaultPocketTpl) + { + return new JsonObject + { + ["_id"] = new MongoId().ToString(), + ["_tpl"] = defaultPocketTpl, + ["parentId"] = parentId, + ["slotId"] = "Pockets", + }; + } + + // Set slotId to hideout, parentId to sorting table & remove location so that the sorting table will automatically pick a location + private void MoveItemsToSortingTable(JsonArray items, string sortingTableId) + { + foreach (var item in items.OfType()) + { + if ( + item.TryGetPropertyValue("slotId", out var slotNode) + && slotNode is JsonValue slotNodeValue + && slotNodeValue.TryGetValue(out var slotId) + && ( + ( + slotId.StartsWith("pocket", StringComparison.OrdinalIgnoreCase) + // Exclude the pcokets itself + && !slotId.Equals("Pockets", StringComparison.OrdinalIgnoreCase) + ) + // Special slots are also keyed to the pockets + || slotId.StartsWith("SpecialSlot", StringComparison.OrdinalIgnoreCase) + ) + ) + { + item["slotId"] = "hideout"; + item["parentId"] = sortingTableId; + item.Remove("location"); + } + } + } + + public override bool CanMigrate(JsonObject profile, IEnumerable previouslyRanMigrations) + { + if (GetPmcPocketStatus(profile) != PocketStatus.Valid || GetScavPocketStatus(profile) != PocketStatus.Valid) + { + return true; + } + + return false; + } + + public override JsonObject? Migrate(JsonObject profile) + { + var pmcPocketStatus = GetPmcPocketStatus(profile); + var scavPocketStatus = GetScavPocketStatus(profile); + + if (pmcPocketStatus != PocketStatus.Valid) + { + var items = profile["characters"]?["pmc"]?["Inventory"]?["items"] as JsonArray; + var pmcInventory = profile["characters"]?["pmc"]?["Inventory"] as JsonObject; + var pmcSortingTable = pmcInventory?["sortingTable"]?.GetValue()!; + var pmcEquipment = pmcInventory?["equipment"]?.GetValue(); + + var pmcPocketTpl = DEFAULT_POCKETS; + + if (IsUnheardProfile(profile) || HasCompletedOldPatterns(profile)) + { + pmcPocketTpl = UNHEARD_POCKETS; + } + + if (pmcPocketStatus == PocketStatus.Missing) + { + if (items != null && pmcEquipment != null) + { + items.Add(CreatePocketItem(pmcEquipment, pmcPocketTpl)); + MoveItemsToSortingTable(items, pmcSortingTable); + } + } + else if (pmcPocketStatus == PocketStatus.Invalid) + { + foreach (var item in items.OfType()) + { + if ( + item.TryGetPropertyValue("slotId", out var slotNode) + && slotNode is JsonValue slotNodeValue + && slotNodeValue.TryGetValue(out var slotId) + && slotId == "Pockets" + ) + { + item["_tpl"] = pmcPocketTpl; + + MoveItemsToSortingTable(items, pmcSortingTable); + } + } + } + } + + if (scavPocketStatus != PocketStatus.Valid) + { + var scavItems = profile["characters"]?["scav"]?["Inventory"]?["items"] as JsonArray; + var scavInventory = profile["characters"]?["scav"]?["Inventory"] as JsonObject; + var scavEquipment = scavInventory?["equipment"]?.GetValue(); + + if (scavPocketStatus == PocketStatus.Missing) + { + if (scavItems != null && scavEquipment != null) + { + scavItems.Add(CreatePocketItem(scavEquipment, DEFAULT_POCKETS)); + } + } + else if (scavPocketStatus == PocketStatus.Invalid) + { + foreach (var item in scavItems.OfType()) + { + if ( + item.TryGetPropertyValue("slotId", out var slotNode) + && slotNode is JsonValue slotNodeValue + && slotNodeValue.TryGetValue(out var slotId) + && slotId == "Pockets" + ) + { + item["_tpl"] = DEFAULT_POCKETS; + } + } + } + } + + return base.Migrate(profile); + } +} diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs index 704fbb777..3c00b3200 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs @@ -2929,6 +2929,9 @@ public record BTRSettings public record BtrMapConfig { + /// + /// Known values: Tarcola, Cleare, Dirt, HeavyDirt + /// [JsonPropertyName("BtrSkin")] public string BtrSkin { get; set; } diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotCore.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotCore.cs deleted file mode 100644 index 9a3a776f2..000000000 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotCore.cs +++ /dev/null @@ -1,399 +0,0 @@ -using System.Text.Json.Serialization; - -namespace SPTarkov.Server.Core.Models.Eft.Common.Tables; - -public record BotCore -{ - [JsonPropertyName("SAVAGE_KILL_DIST")] - public double? SavageKillDistance { get; set; } - - [JsonPropertyName("SOUND_DOOR_BREACH_METERS")] - public double? SoundDoorBreachMeters { get; set; } - - [JsonPropertyName("SOUND_DOOR_OPEN_METERS")] - public double? SoundDoorOpenMeters { get; set; } - - [JsonPropertyName("STEP_NOISE_DELTA")] - public double? StepNoiseDelta { get; set; } - - [JsonPropertyName("JUMP_NOISE_DELTA")] - public double? JumpNoiseDelta { get; set; } - - [JsonPropertyName("GUNSHOT_SPREAD")] - public double? GunshotSpread { get; set; } - - [JsonPropertyName("GUNSHOT_SPREAD_SILENCE")] - public double? GunshotSpreadSilence { get; set; } - - [JsonPropertyName("BASE_WALK_SPEREAD2")] - public double? BaseWalkSpread2 { get; set; } - - [JsonPropertyName("MOVE_SPEED_COEF_MAX")] - public double? MoveSpeedCoefficientMax { get; set; } - - [JsonPropertyName("SPEED_SERV_SOUND_COEF_A")] - public double? SpeedServiceSoundCoefficientA { get; set; } - - [JsonPropertyName("SPEED_SERV_SOUND_COEF_B")] - public double? SpeedServiceSoundCoefficientB { get; set; } - - [JsonPropertyName("G")] - public double? Gravity { get; set; } - - [JsonPropertyName("STAY_COEF")] - public double? StayCoefficient { get; set; } - - [JsonPropertyName("SIT_COEF")] - public double? SitCoefficient { get; set; } - - [JsonPropertyName("LAY_COEF")] - public double? LayCoefficient { get; set; } - - [JsonPropertyName("MAX_ITERATIONS")] - public double? MaxIterations { get; set; } - - [JsonPropertyName("START_DIST_TO_COV")] - public double? StartDistanceToCover { get; set; } - - [JsonPropertyName("MAX_DIST_TO_COV")] - public double? MaxDistanceToCover { get; set; } - - [JsonPropertyName("STAY_HEIGHT")] - public double? StayHeight { get; set; } - - [JsonPropertyName("CLOSE_POINTS")] - public double? ClosePoints { get; set; } - - [JsonPropertyName("COUNT_TURNS")] - public double? CountTurns { get; set; } - - [JsonPropertyName("SIMPLE_POINT_LIFE_TIME_SEC")] - public double? SimplePointLifetimeSeconds { get; set; } - - [JsonPropertyName("DANGER_POINT_LIFE_TIME_SEC")] - public double? DangerPointLifetimeSeconds { get; set; } - - [JsonPropertyName("DANGER_POWER")] - public double? DangerPower { get; set; } - - [JsonPropertyName("COVER_DIST_CLOSE")] - public double? CoverDistanceClose { get; set; } - - [JsonPropertyName("GOOD_DIST_TO_POINT")] - public double? GoodDistanceToPoint { get; set; } - - [JsonPropertyName("COVER_TOOFAR_FROM_BOSS")] - public double? CoverTooFarFromBoss { get; set; } - - [JsonPropertyName("COVER_TOOFAR_FROM_BOSS_SQRT")] - public double? CoverTooFarFromBossSqrt { get; set; } - - [JsonPropertyName("MAX_Y_DIFF_TO_PROTECT")] - public double? MaxYDifferenceToProtect { get; set; } - - [JsonPropertyName("FLARE_POWER")] - public double? FlarePower { get; set; } - - [JsonPropertyName("MOVE_COEF")] - public double? MoveCoefficient { get; set; } - - [JsonPropertyName("PRONE_POSE")] - public double? PronePose { get; set; } - - [JsonPropertyName("LOWER_POSE")] - public double? LowerPose { get; set; } - - [JsonPropertyName("MAX_POSE")] - public double? MaxPose { get; set; } - - [JsonPropertyName("FLARE_TIME")] - public double? FlareTime { get; set; } - - [JsonPropertyName("MAX_REQUESTS__PER_GROUP")] - public double? MaxRequestsPerGroup { get; set; } - - [JsonPropertyName("UPDATE_GOAL_TIMER_SEC")] - public double? UpdateGoalTimerSeconds { get; set; } - - [JsonPropertyName("DIST_NOT_TO_GROUP")] - public double? DistanceNotToGroup { get; set; } - - [JsonPropertyName("DIST_NOT_TO_GROUP_SQR")] - public double? DistanceNotToGroupSquared { get; set; } - - [JsonPropertyName("LAST_SEEN_POS_LIFETIME")] - public double? LastSeenPositionLifetime { get; set; } - - [JsonPropertyName("DELTA_GRENADE_START_TIME")] - public double? DeltaGrenadeStartTime { get; set; } - - [JsonPropertyName("DELTA_GRENADE_END_TIME")] - public double? DeltaGrenadeEndTime { get; set; } - - [JsonPropertyName("DELTA_GRENADE_RUN_DIST")] - public double? DeltaGrenadeRunDistance { get; set; } - - [JsonPropertyName("DELTA_GRENADE_RUN_DIST_SQRT")] - public double? DeltaGrenadeRunDistanceSqrt { get; set; } - - [JsonPropertyName("PATROL_MIN_LIGHT_DIST")] - public double? PatrolMinimumLightDistance { get; set; } - - [JsonPropertyName("HOLD_MIN_LIGHT_DIST")] - public double? HoldMinimumLightDistance { get; set; } - - [JsonPropertyName("STANDART_BOT_PAUSE_DOOR")] - public double? StandardBotPauseDoor { get; set; } - - [JsonPropertyName("ARMOR_CLASS_COEF")] - public double? ArmorClassCoefficient { get; set; } - - [JsonPropertyName("SHOTGUN_POWER")] - public double? ShotgunPower { get; set; } - - [JsonPropertyName("RIFLE_POWER")] - public double? RiflePower { get; set; } - - [JsonPropertyName("PISTOL_POWER")] - public double? PistolPower { get; set; } - - [JsonPropertyName("SMG_POWER")] - public double? SMGPower { get; set; } - - [JsonPropertyName("SNIPE_POWER")] - public double? SniperPower { get; set; } - - [JsonPropertyName("GESTUS_PERIOD_SEC")] - public double? GestusPeriodSeconds { get; set; } - - [JsonPropertyName("GESTUS_AIMING_DELAY")] - public double? GestusAimingDelay { get; set; } - - [JsonPropertyName("GESTUS_REQUEST_LIFETIME")] - public double? GestusRequestLifetime { get; set; } - - [JsonPropertyName("GESTUS_FIRST_STAGE_MAX_TIME")] - public double? GestusFirstStageMaxTime { get; set; } - - [JsonPropertyName("GESTUS_SECOND_STAGE_MAX_TIME")] - public double? GestusSecondStageMaxTime { get; set; } - - [JsonPropertyName("GESTUS_MAX_ANSWERS")] - public double? GestusMaxAnswers { get; set; } - - [JsonPropertyName("GESTUS_FUCK_TO_SHOOT")] - public double? GestusFuckToShoot { get; set; } - - [JsonPropertyName("GESTUS_DIST_ANSWERS")] - public double? GestusDistanceAnswers { get; set; } - - [JsonPropertyName("GESTUS_DIST_ANSWERS_SQRT")] - public double? GestusDistanceAnswersSqrt { get; set; } - - [JsonPropertyName("GESTUS_ANYWAY_CHANCE")] - public double? GestusAnywayChance { get; set; } - - [JsonPropertyName("TALK_DELAY")] - public double? TalkDelay { get; set; } - - [JsonPropertyName("CAN_SHOOT_TO_HEAD")] - public bool? CanShootToHead { get; set; } - - [JsonPropertyName("CAN_TILT")] - public bool? CanTilt { get; set; } - - [JsonPropertyName("TILT_CHANCE")] - public double? TiltChance { get; set; } - - [JsonPropertyName("MIN_BLOCK_DIST")] - public double? MinimumBlockDistance { get; set; } - - [JsonPropertyName("MIN_BLOCK_TIME")] - public double? MinimumBlockTime { get; set; } - - [JsonPropertyName("COVER_SECONDS_AFTER_LOSE_VISION")] - public double? CoverSecondsAfterLoseVision { get; set; } - - [JsonPropertyName("MIN_ARG_COEF")] - public double? MinimumArgumentCoefficient { get; set; } - - [JsonPropertyName("MAX_ARG_COEF")] - public double? MaximumArgumentCoefficient { get; set; } - - [JsonPropertyName("DEAD_AGR_DIST")] - public double? DeadAgrDistance { get; set; } - - [JsonPropertyName("MAX_DANGER_CARE_DIST_SQRT")] - public double? MaxDangerCareDistanceSqrt { get; set; } - - [JsonPropertyName("MAX_DANGER_CARE_DIST")] - public double? MaxDangerCareDistance { get; set; } - - [JsonPropertyName("MIN_MAX_PERSON_SEARCH")] - public double? MinimumMaximumPersonSearch { get; set; } - - [JsonPropertyName("PERCENT_PERSON_SEARCH")] - public double? PercentPersonSearch { get; set; } - - [JsonPropertyName("LOOK_ANYSIDE_BY_WALL_SEC_OF_ENEMY")] - public double? LookAnySideByWallSecondsOfEnemy { get; set; } - - [JsonPropertyName("CLOSE_TO_WALL_ROTATE_BY_WALL_SQRT")] - public double? CloseToWallRotateByWallSqrt { get; set; } - - [JsonPropertyName("SHOOT_TO_CHANGE_RND_PART_MIN")] - public double? ShootToChangeRandomPartMinimum { get; set; } - - [JsonPropertyName("SHOOT_TO_CHANGE_RND_PART_MAX")] - public double? ShootToChangeRandomPartMaximum { get; set; } - - [JsonPropertyName("SHOOT_TO_CHANGE_RND_PART_DELTA")] - public double? ShootToChangeRandomPartDelta { get; set; } - - [JsonPropertyName("FORMUL_COEF_DELTA_DIST")] - public double? FormulaCoefficientDeltaDistance { get; set; } - - [JsonPropertyName("FORMUL_COEF_DELTA_SHOOT")] - public double? FormulaCoefficientDeltaShoot { get; set; } - - [JsonPropertyName("FORMUL_COEF_DELTA_FRIEND_COVER")] - public double? FormulaCoefficientDeltaFriendCover { get; set; } - - [JsonPropertyName("SUSPETION_POINT_DIST_CHECK")] - public double? SuspicionPointDistanceCheck { get; set; } - - [JsonPropertyName("MAX_BASE_REQUESTS_PER_PLAYER")] - public double? MaxBaseRequestsPerPlayer { get; set; } - - [JsonPropertyName("MAX_HOLD_REQUESTS_PER_PLAYER")] - public double? MaxHoldRequestsPerPlayer { get; set; } - - [JsonPropertyName("MAX_GO_TO_REQUESTS_PER_PLAYER")] - public double? MaxGoToRequestsPerPlayer { get; set; } - - [JsonPropertyName("MAX_COME_WITH_ME_REQUESTS_PER_PLAYER")] - public double? MaxComeWithMeRequestsPerPlayer { get; set; } - - [JsonPropertyName("CORE_POINT_MAX_VALUE")] - public double? CorePointMaxValue { get; set; } - - [JsonPropertyName("CORE_POINTS_MAX")] - public double? CorePointsMax { get; set; } - - [JsonPropertyName("CORE_POINTS_MIN")] - public double? CorePointsMin { get; set; } - - [JsonPropertyName("BORN_POISTS_FREE_ONLY_FAREST_BOT")] - public bool? BornPointsFreeOnlyFarthestBot { get; set; } - - [JsonPropertyName("BORN_POINSTS_FREE_ONLY_FAREST_PLAYER")] - public bool? BornPointsFreeOnlyFarthestPlayer { get; set; } - - [JsonPropertyName("SCAV_GROUPS_TOGETHER")] - public bool? ScavGroupsTogether { get; set; } - - [JsonPropertyName("LAY_DOWN_ANG_SHOOT")] - public double? LayDownAngleShoot { get; set; } - - [JsonPropertyName("HOLD_REQUEST_TIME_SEC")] - public double? HoldRequestTimeSeconds { get; set; } - - [JsonPropertyName("TRIGGERS_DOWN_TO_RUN_WHEN_MOVE")] - public double? TriggersDownToRunWhenMove { get; set; } - - [JsonPropertyName("MIN_DIST_TO_RUN_WHILE_ATTACK_MOVING")] - public double? MinimumDistanceToRunWhileAttackingMoving { get; set; } - - [JsonPropertyName("MIN_DIST_TO_RUN_WHILE_ATTACK_MOVING_OTHER_ENEMIS")] - public double? MinimumDistanceToRunWhileAttackingMovingOtherEnemies { get; set; } - - [JsonPropertyName("MIN_DIST_TO_STOP_RUN")] - public double? MinimumDistanceToStopRunning { get; set; } - - [JsonPropertyName("JUMP_SPREAD_DIST")] - public double? JumpSpreadDistance { get; set; } - - [JsonPropertyName("LOOK_TIMES_TO_KILL")] - public double? LookTimesToKill { get; set; } - - [JsonPropertyName("COME_INSIDE_TIMES")] - public double? ComeInsideTimes { get; set; } - - [JsonPropertyName("TOTAL_TIME_KILL")] - public double? TotalTimeKill { get; set; } - - [JsonPropertyName("TOTAL_TIME_KILL_AFTER_WARN")] - public double? TotalTimeKillAfterWarning { get; set; } - - [JsonPropertyName("MOVING_AIM_COEF")] - public double? MovingAimCoefficient { get; set; } - - [JsonPropertyName("VERTICAL_DIST_TO_IGNORE_SOUND")] - public double? VerticalDistanceToIgnoreSound { get; set; } - - [JsonPropertyName("DEFENCE_LEVEL_SHIFT")] - public double? DefenseLevelShift { get; set; } - - [JsonPropertyName("MIN_DIST_CLOSE_DEF")] - public double? MinimumDistanceCloseDefense { get; set; } - - [JsonPropertyName("USE_ID_PRIOR_WHO_GO")] - public bool? UseIdPriorWhoGoes { get; set; } - - [JsonPropertyName("SMOKE_GRENADE_RADIUS_COEF")] - public double? SmokeGrenadeRadiusCoefficient { get; set; } - - [JsonPropertyName("GRENADE_PRECISION")] - public double? GrenadePrecision { get; set; } - - [JsonPropertyName("MAX_WARNS_BEFORE_KILL")] - public double? MaxWarningsBeforeKill { get; set; } - - [JsonPropertyName("CARE_ENEMY_ONLY_TIME")] - public double? CareEnemyOnlyTime { get; set; } - - [JsonPropertyName("MIDDLE_POINT_COEF")] - public double? MiddlePointCoefficient { get; set; } - - [JsonPropertyName("MAIN_TACTIC_ONLY_ATTACK")] - public bool? MainTacticOnlyAttack { get; set; } - - [JsonPropertyName("LAST_DAMAGE_ACTIVE")] - public double? LastDamageActive { get; set; } - - [JsonPropertyName("SHALL_DIE_IF_NOT_INITED")] - public bool? ShallDieIfNotInitialized { get; set; } - - [JsonPropertyName("CHECK_BOT_INIT_TIME_SEC")] - public double? CheckBotInitializationTimeSeconds { get; set; } - - [JsonPropertyName("WEAPON_ROOT_Y_OFFSET")] - public double? WeaponRootYOffset { get; set; } - - [JsonPropertyName("DELTA_SUPRESS_DISTANCE_SQRT")] - public double? DeltaSuppressDistanceSqrt { get; set; } - - [JsonPropertyName("DELTA_SUPRESS_DISTANCE")] - public double? DeltaSuppressDistance { get; set; } - - [JsonPropertyName("WAVE_COEF_LOW")] - public double? WaveCoefficientLow { get; set; } - - [JsonPropertyName("WAVE_COEF_MID")] - public double? WaveCoefficientMid { get; set; } - - [JsonPropertyName("WAVE_COEF_HIGH")] - public double? WaveCoefficientHigh { get; set; } - - [JsonPropertyName("WAVE_COEF_HORDE")] - public double? WaveCoefficientHorde { get; set; } - - [JsonPropertyName("WAVE_ONLY_AS_ONLINE")] - public bool? WaveOnlyAsOnline { get; set; } - - [JsonPropertyName("LOCAL_BOTS_COUNT")] - public double? LocalBotsCount { get; set; } - - [JsonPropertyName("AXE_MAN_KILLS_END")] - public double? AxeManKillsEnd { get; set; } -} diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/RagfairConfig.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/RagfairConfig.cs index fafb706fa..52cc708cf 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Config/RagfairConfig.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Config/RagfairConfig.cs @@ -269,6 +269,12 @@ public record GenerateFleaPrices [JsonPropertyName("hideoutCraftMultiplier")] public double HideoutCraftMultiplier { get; set; } + + /// + /// Should weapons/armors have their price generated by totalling its child items + /// + [JsonPropertyName("generatePresetPriceByChildren")] + public bool GeneratePresetPriceByChildren { get; set; } } public record PriceRanges diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2RemoveResponse.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2RemoveResponse.cs index 70194641a..4e2b10537 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2RemoveResponse.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2RemoveResponse.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using SPTarkov.Server.Core.Models.Eft.Launcher; using SPTarkov.Server.Core.Models.Utils; diff --git a/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2WipeResponse.cs b/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2WipeResponse.cs new file mode 100644 index 000000000..fcd1944b0 --- /dev/null +++ b/Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2WipeResponse.cs @@ -0,0 +1,11 @@ +using SPTarkov.Server.Core.Models.Eft.Launcher; +using SPTarkov.Server.Core.Models.Utils; + +namespace SPTarkov.Server.Core.Models.Spt.Launcher; + +public record LauncherV2WipeResponse : IRequestData +{ + public required bool Response { get; set; } + + public required List Profiles { get; set; } +} diff --git a/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs index 3dd14da9b..95324cb7c 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs @@ -12,32 +12,15 @@ public class LauncherV2StaticRouter(LauncherV2Callbacks launcherV2Callbacks, Jso : StaticRouter( jsonUtil, [ - new RouteAction("/launcher/v2/ping", async (url, _, sessionID, _) => await launcherV2Callbacks.Ping()), - new RouteAction("/launcher/v2/types", async (url, _, sessionID, _) => await launcherV2Callbacks.Types()), - new RouteAction( - "/launcher/v2/login", - async (url, info, sessionID, _) => await launcherV2Callbacks.Login(info) - ), - new RouteAction( - "/launcher/v2/register", - async (url, info, sessionID, _) => await launcherV2Callbacks.Register(info) - ), - new RouteAction( - "/launcher/v2/remove", - async (url, info, sessionID, _) => await launcherV2Callbacks.Remove(info) - ), - new RouteAction( - "/launcher/v2/version", - async (url, _, sessionID, _) => await launcherV2Callbacks.CompatibleVersion() - ), - new RouteAction("/launcher/v2/mods", async (url, _, sessionID, _) => await launcherV2Callbacks.Mods()), - new RouteAction( - "/launcher/v2/profiles", - async (url, _, sessionID, _) => await launcherV2Callbacks.Profiles() - ), - new RouteAction( - "/launcher/v2/profile", - async (url, info, sessionID, _) => await launcherV2Callbacks.Profile(info) - ), + new RouteAction("/launcher/v2/ping", async (_, _, _, _) => await launcherV2Callbacks.Ping()), + new RouteAction("/launcher/v2/types", async (_, _, _, _) => await launcherV2Callbacks.Types()), + new RouteAction("/launcher/v2/login", async (_, info, _, _) => await launcherV2Callbacks.Login(info)), + new RouteAction("/launcher/v2/register", async (_, info, _, _) => await launcherV2Callbacks.Register(info)), + new RouteAction("/launcher/v2/remove", async (_, info, _, _) => await launcherV2Callbacks.Remove(info)), + new RouteAction("/launcher/v2/version", async (_, _, _, _) => await launcherV2Callbacks.CompatibleVersion()), + new RouteAction("/launcher/v2/mods", async (_, _, _, _) => await launcherV2Callbacks.Mods()), + new RouteAction("/launcher/v2/profiles", async (_, _, _, _) => await launcherV2Callbacks.Profiles()), + new RouteAction("/launcher/v2/profile", async (_, info, _, _) => await launcherV2Callbacks.Profile(info)), + new RouteAction("/launcher/v2/wipe", async (_, info, _, _) => await launcherV2Callbacks.Wipe(info)), ] ) { } diff --git a/Libraries/SPTarkov.Server.Core/Services/Mod/ProfileDataService.cs b/Libraries/SPTarkov.Server.Core/Services/Mod/ProfileDataService.cs index 486b427d5..288be4646 100644 --- a/Libraries/SPTarkov.Server.Core/Services/Mod/ProfileDataService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/Mod/ProfileDataService.cs @@ -11,22 +11,27 @@ public class ProfileDataService(ISptLogger logger, FileUtil protected const string ProfileDataFilepath = "user/profileData/"; private readonly ConcurrentDictionary _profileDataCache = new(); + /// + /// Check if a specfici mod file exists for a profile + /// + /// Profile to look up + /// Name of json file to look up public bool ProfileDataExists(string profileId, string modKey) { - return fileUtil.FileExists($"{ProfileDataFilepath}{profileId}/{modKey}.json"); + return fileUtil.FileExists(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json")); } public T? GetProfileData(string profileId, string modKey) { - var profileDataKey = $"{profileId}:{modKey}"; + var profileDataKey = GetCacheKey(profileId, modKey); if (!_profileDataCache.TryGetValue(profileDataKey, out var value)) { - if (fileUtil.FileExists($"{ProfileDataFilepath}{profileId}/{modKey}.json")) + if (ProfileDataExists(profileId, modKey)) { - value = jsonUtil.Deserialize(fileUtil.ReadFile($"{ProfileDataFilepath}{profileId}/{modKey}.json")); + value = jsonUtil.Deserialize(fileUtil.ReadFile(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json"))); if (value != null) { - _profileDataCache[$"{profileId}:{modKey}"] = value; + _profileDataCache[GetCacheKey(profileId, modKey)] = value; } } else @@ -42,13 +47,45 @@ public void SaveProfileData(string profileId, string modKey, T profileData) { ArgumentNullException.ThrowIfNull(profileData); - var data = jsonUtil.Serialize(profileData, profileData.GetType(), true); - if (data == null) + var data = + jsonUtil.Serialize(profileData, profileData.GetType(), true) + ?? throw new Exception("The profile data when serialized resulted in a null value"); + + _profileDataCache[GetCacheKey(profileId, modKey)] = profileData; + + fileUtil.WriteFile(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json"), data); + } + + /// + /// Clear all data for a profile + /// + /// Id of profile to delete files for + public void ClearProfileData(string profileId) + { + if (!fileUtil.DirectoryExists(Path.Combine(ProfileDataFilepath, profileId))) + { + return; + } + + var profileFiles = fileUtil.GetFiles(Path.Combine(ProfileDataFilepath, profileId)); + foreach (var filepPath in profileFiles) { - throw new Exception("The profile data when serialized resulted in a null value"); + fileUtil.DeleteFile(filepPath); } - _profileDataCache[$"{profileId}:{modKey}"] = profileData; - fileUtil.WriteFile($"{ProfileDataFilepath}{profileId}/{modKey}.json", data); + var keysInCacheToRemove = _profileDataCache.Keys.Where(key => key.StartsWith($"{profileId}:")).ToList(); // ToList so we can iterate over results without modifying collection + + foreach (var key in keysInCacheToRemove) + { + _profileDataCache.TryRemove(key, out _); + } + } + + /// + /// Get the cache key in specific format + /// + protected string GetCacheKey(string profileId, string modKey) + { + return $"{profileId}:{modKey}"; } } diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairPriceService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairPriceService.cs index 659f6edbd..4c5668eaf 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairPriceService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairPriceService.cs @@ -272,7 +272,7 @@ public double GetDynamicOfferPriceForOffer(IEnumerable offerItems, MongoId continue; } - price += GetDynamicItemPrice(item.Template, desiredCurrency, item, offerItems, isPackOffer).Value; + price += GetDynamicItemPrice(item.Template, desiredCurrency, item, offerItems, isPackOffer) ?? 0; // Check if the item is a weapon preset. if (item?.Upd?.SptPresetId is not null && presetHelper.IsPresetBaseClass(item.Upd.SptPresetId.Value, BaseClasses.WEAPON)) @@ -329,7 +329,11 @@ public double GetDynamicOfferPriceForOffer(IEnumerable offerItems, MongoId && presetHelper.IsPresetBaseClass(item.Upd.SptPresetId.Value, BaseClasses.WEAPON) ) { - price = GetWeaponPresetPrice(item, offerItems, price); + price = + RagfairConfig.Dynamic.GenerateBaseFleaPrices.UseHandbookPrice + && RagfairConfig.Dynamic.GenerateBaseFleaPrices.GeneratePresetPriceByChildren + ? GetPresetPriceByChildren(offerItems) + : GetWeaponPresetPrice(item, offerItems, price); isPreset = true; } @@ -522,6 +526,28 @@ protected double GetWeaponPresetPrice(Item weaponRootItem, IEnumerable wea return existingPrice + extraModsPrice; } + /// + /// Calculate the cost of a weapon preset by adding together the price of its mods + /// + /// weapon plus mods + /// price of weapon in roubles + protected double GetPresetPriceByChildren(IEnumerable weaponWithChildren) + { + var priceTotal = 0d; + foreach (var item in weaponWithChildren) + { + // Root item uses static price + if (item.ParentId == null) + { + priceTotal += GetStaticPriceForItem(item.Template) ?? 0; + } + + priceTotal += GetFleaPriceForItem(item.Template); + } + + return priceTotal; + } + /// /// Get the highest price for an item that is stored in handbook or trader assorts /// diff --git a/Libraries/SPTarkov.Server.Core/Services/ReleaseCheckService.cs b/Libraries/SPTarkov.Server.Core/Services/ReleaseCheckService.cs new file mode 100644 index 000000000..36b500438 --- /dev/null +++ b/Libraries/SPTarkov.Server.Core/Services/ReleaseCheckService.cs @@ -0,0 +1,76 @@ +using System.Net.Http.Json; +using System.Text.Json.Serialization; +using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.DI; +using SPTarkov.Server.Core.Models.Utils; +using SPTarkov.Server.Core.Utils; +using Range = SemanticVersioning.Range; +using Version = SemanticVersioning.Version; + +namespace SPTarkov.Server.Core.Services; + +// Note: We want to run after all mods, to avoid this being lost in mod log +// spam, so we purposely use MaxValue here + +[Injectable(TypePriority = int.MaxValue)] +internal class ReleaseCheckService(ISptLogger logger) : IOnLoad +{ + public Task OnLoad() + { + // Run in a new task so we don't hold the main thread at all, this isn't super critical + _ = Task.Run(CheckForUpdate); + + return Task.CompletedTask; + } + + private async Task CheckForUpdate() + { + try + { + var httpClient = new HttpClient(); + + // These headers are _required_ by GitHub API + httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("SP-Tarkov"); + httpClient.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28"); + + // TODO: We could probably throw this into a config somewhere, for now hard code it + var release = await httpClient.GetFromJsonAsync( + "https://api.github.com/repos/sp-tarkov/build/releases/latest" + ); + if (release != null) + { + Version latestVersion = new(release.Version); + Version currentVersion = ProgramStatics.SPT_VERSION(); + Range currentVersionRange = new($"~{currentVersion.Major}.{currentVersion.Minor}.0"); + + // First make sure the latest release is in our range, this stops "4.1.0" from being detected as a valid upgrade for "4.0.1" + if (!currentVersionRange.IsSatisfied(latestVersion)) + { + return; + } + + // Notify the user if an upgrade is available + if (latestVersion > currentVersion) + { + logger.Warning($"A new version of SPT is available! SPT v{release.Version}"); + logger.Warning($"Released {release.ReleaseDate.ToLocalTime()}"); + logger.Warning($"Release Notes: {release.DownloadUrl}"); + } + } + } + // We ignore errors, this isn't critical to run, and we don't want to scare users + catch { } + } + + private record ReleaseInformation + { + [JsonPropertyName("tag_name")] + public required string Version { get; init; } + + [JsonPropertyName("html_url")] + public required string DownloadUrl { get; init; } + + [JsonPropertyName("published_at")] + public required DateTime ReleaseDate { get; init; } + } +} diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index a408a21cc..aa20bf0b9 100644 --- a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs @@ -407,6 +407,7 @@ protected void UpdateGlobalEvents(Config globalConfig, SeasonalEvent eventType) AddEventBossesToMaps("halloweensummon"); EnableHalloweenSummonEvent(); AddPumpkinsToScavBackpacks(); + AddEventBossesToMaps("halloweennightcult"); RenameBitcoin(); if (eventType.Settings is not null && eventType.Settings.ReplaceBotHostility.GetValueOrDefault(false)) { @@ -457,7 +458,10 @@ protected void ApplyHalloweenEvent(SeasonalEvent eventType, Config globalConfig) if (eventType.Settings?.ReplaceBotHostility ?? false) { - ReplaceBotHostility(SeasonalEventConfig.HostilitySettingsForEvent.FirstOrDefault(x => x.Key == "zombies").Value); + ReplaceBotHostility( + SeasonalEventConfig.HostilitySettingsForEvent.FirstOrDefault(x => x.Key == "zombies").Value, + GetLocationsWithZombies(eventType.Settings.ZombieSettings.MapInfectionAmount) + ); } if (eventType.Settings?.AdjustBotAppearances ?? false) @@ -477,6 +481,8 @@ protected void ApplyHalloweenEvent(SeasonalEvent eventType, Config globalConfig) { usec.BotAppearance.Head[new MongoId("6644d2da35d958070c02642c")] = 30; } + + AddEventBossesToMaps("halloweennightcult"); } protected void ApplyChristmasEvent(SeasonalEvent eventType, Config globalConfig) @@ -507,6 +513,23 @@ protected void ApplyChristmasEvent(SeasonalEvent eventType, Config globalConfig) { AdjustBotAppearanceValues(eventType.Type); } + + ChangeBtrToTarColaSkin(); + } + + private void ChangeBtrToTarColaSkin() + { + var btrSettings = databaseService.GetGlobals().Configuration.BTRSettings; + + if (btrSettings.MapsConfigs.TryGetValue("Woods", out var woodsBtrSettings)) + { + woodsBtrSettings.BtrSkin = "Tarcola"; + } + + if (btrSettings.MapsConfigs.TryGetValue("TarkovStreets", out var streetsBtrSettings)) + { + streetsBtrSettings.BtrSkin = "Tarcola"; + } } protected void ApplyNewYearsEvent(SeasonalEvent eventType, Config globalConfig) @@ -585,7 +608,10 @@ protected void AdjustBotAppearanceValues(SeasonalEventType season) } } - protected void ReplaceBotHostility(Dictionary> hostilitySettings) + protected void ReplaceBotHostility( + Dictionary> hostilitySettings, + HashSet? locationWhitelist = null + ) { var locations = databaseService.GetLocations().GetDictionary(); var ignoreList = LocationConfig.NonMaps; @@ -613,6 +639,11 @@ protected void ReplaceBotHostility(Dictionary @@ -870,7 +901,7 @@ protected void AddEventBossesToMaps(string eventType, HashSet? mapIdWhit continue; } - if (mapIdWhitelist is null || !mapIdWhitelist.Contains(locationKey)) + if (mapIdWhitelist is not null && !mapIdWhitelist.Contains(locationKey)) { continue; } @@ -881,7 +912,7 @@ protected void AddEventBossesToMaps(string eventType, HashSet? mapIdWhit { if (mapBosses.All(bossSpawn => bossSpawn.BossName != boss.BossName)) { - // Zombie doesn't exist in maps boss list yet, add + // Boss doesn't exist in maps boss list yet, add mapBosses.Add(boss); } } diff --git a/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs index bd317b957..2fe0d3cfc 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs @@ -135,14 +135,7 @@ public async Task WriteFileAsync(string filePath, byte[] fileContent) } // Overwrite over the old file - if (File.Exists(filePath)) - { - File.Replace(tempFilePath, filePath, null); - } - else - { - File.Move(tempFilePath, filePath); - } + File.Move(tempFilePath, filePath, overwrite: true); } catch { diff --git a/SPTarkov.Server/Modding/ModValidator.cs b/SPTarkov.Server/Modding/ModValidator.cs index 88e4e0e01..40ee87003 100644 --- a/SPTarkov.Server/Modding/ModValidator.cs +++ b/SPTarkov.Server/Modding/ModValidator.cs @@ -18,6 +18,12 @@ public List ValidateMods(IEnumerable mods) return []; } + // Validate all assemblies for references. This will deprecate AbstractMetadata semver checks in 4.1 + foreach (var mod in mods) + { + ValidateCoreAssemblyReference(mod); + } + logger.Info(localisationService.GetText("modloader-loading_mods", mods.Count())); // Validate and remove broken mods from mod list @@ -147,6 +153,39 @@ protected bool IsModCompatibleWithSpt(AbstractModMetadata mod) return true; } + /// + /// Validate that the SPTarkov.Server.Core assembly is compatible with this mod. Semver is not enough.
+ /// + /// Throws an exception if the mod was built for a newer SPT version than the current running SPT version + ///
+ /// mod to validate + protected void ValidateCoreAssemblyReference(SptMod mod) + { + var sptVersion = ProgramStatics.SPT_VERSION(); + var modName = $"{mod.ModMetadata.Author}-{mod.ModMetadata.Name}"; + + foreach (var assembly in mod.Assemblies) + { + var sptCoreAsmRefVersion = assembly + .GetReferencedAssemblies() + .FirstOrDefault(asm => asm.Name == "SPTarkov.Server.Core") + ?.Version?.ToString(); + + if (sptCoreAsmRefVersion is null) + { + continue; + } + + var modRefVersion = new SemanticVersioning.Version(sptCoreAsmRefVersion?[..^2]!); + if (modRefVersion > sptVersion) + { + throw new Exception( + $"Mod: {modName} requires a minimum SPT version of `{modRefVersion}`, but you are running `{sptVersion}`. Please update SPT to use this mod." + ); + } + } + } + /// /// Add into class property "Imported" ///