From 7039731e6700291acfb72dc52c4e6400bc82fa69 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Date: Sun, 26 Oct 2025 23:04:30 -0700 Subject: [PATCH 01/36] Fix exception sometimes thrown on save - Switch back from File.Rename to File.Move, as Rename is throwing exceptions on some users systems --- Libraries/SPTarkov.Server.Core/Utils/FileUtil.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) 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 { From 88037291d96de630adf77a0d980df399c9a0b4dd Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 27 Oct 2025 13:32:46 +0000 Subject: [PATCH 02/36] Change BTR skin to tarcola during Christmas event --- .../Services/SeasonalEventService.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index a408a21cc..6fb35ab15 100644 --- a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs @@ -507,6 +507,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) From 17c8572bed67d3b2751f8a3237334697e7554d3d Mon Sep 17 00:00:00 2001 From: Chomp Date: Mon, 27 Oct 2025 13:34:22 +0000 Subject: [PATCH 03/36] Added comment --- Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs | 3 +++ 1 file changed, 3 insertions(+) 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; } From 2d5535c0c865338cf009aca7495de9104ffd8e36 Mon Sep 17 00:00:00 2001 From: CWX Date: Tue, 28 Oct 2025 17:01:12 +0000 Subject: [PATCH 04/36] Remove unused using --- .../Models/Spt/Launcher/LauncherV2RemoveResponse.cs | 1 - 1 file changed, 1 deletion(-) 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; From 9ce50c697a250972eb58f26ae1df02ce34c5e4a6 Mon Sep 17 00:00:00 2001 From: CWX Date: Tue, 28 Oct 2025 17:02:33 +0000 Subject: [PATCH 05/36] Add wipe Response model --- .../Models/Spt/Launcher/LauncherV2WipeResponse.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Libraries/SPTarkov.Server.Core/Models/Spt/Launcher/LauncherV2WipeResponse.cs 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; } +} From abb844c9ce7ba0a7d88d6dd1f67e8edf819822da Mon Sep 17 00:00:00 2001 From: CWX Date: Tue, 28 Oct 2025 17:02:55 +0000 Subject: [PATCH 06/36] formatting and add Wipe Endpoint to V2 --- .../Callbacks/LauncherV2Callbacks.cs | 49 ++++++++++++++++--- .../Controllers/LauncherV2Controller.cs | 22 +++++++++ .../Routers/Static/LauncherV2StaticRouter.cs | 26 ++++++---- 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs index cc8e7a8c4..b4b559180 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; @@ -15,17 +16,26 @@ ProfileController profileController { public ValueTask Ping() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2PingResponse { Response = launcherV2Controller.Ping() })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2PingResponse + { + Response = launcherV2Controller.Ping() + })); } public ValueTask Types() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2TypesResponse { Response = launcherV2Controller.Types() })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2TypesResponse + { + Response = launcherV2Controller.Types() + })); } public ValueTask Login(LoginRequestData info) { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2LoginResponse { Response = launcherV2Controller.Login(info) })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2LoginResponse + { + Response = launcherV2Controller.Login(info) + })); } public async ValueTask Register(RegisterData info) @@ -55,26 +65,49 @@ public ValueTask Remove(LoginRequestData info) public ValueTask CompatibleVersion() { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2VersionResponse { Response = launcherV2Controller.SptVersion() }) + httpResponseUtil.NoBody(new LauncherV2VersionResponse + { + Response = launcherV2Controller.SptVersion() + }) ); } public ValueTask Mods() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2ModsResponse { Response = launcherV2Controller.LoadedMods() })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2ModsResponse + { + Response = launcherV2Controller.LoadedMods() + })); } public ValueTask Profiles() { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2ProfilesResponse { Response = profileController.GetMiniProfiles() }) + httpResponseUtil.NoBody(new LauncherV2ProfilesResponse + { + Response = profileController.GetMiniProfiles() + }) + ); + } + + public ValueTask Profile(LoginRequestData username) + { + return new ValueTask( + httpResponseUtil.NoBody(new LauncherV2ProfileResponse + { + Response = launcherV2Controller.GetMiniProfileFromUsername(username) + }) ); } - public ValueTask Profile(LoginRequestData sessionId) + public ValueTask Wipe(RegisterData info) { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2ProfileResponse { Response = launcherV2Controller.GetMiniProfileFromUsername(sessionId) }) + httpResponseUtil.NoBody(new LauncherV2WipeResponse + { + Response = launcherV2Controller.Wipe(info), + Profiles = profileController.GetMiniProfiles(), + }) ); } } diff --git a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs index 323abf49b..2aba321b6 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs @@ -173,4 +173,26 @@ 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/Routers/Static/LauncherV2StaticRouter.cs b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs index 3dd14da9b..fd5bce760 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs @@ -12,32 +12,38 @@ 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/ping", async (_, _, _, _) => await launcherV2Callbacks.Ping()), + new RouteAction("/launcher/v2/types", async (_, _, _, _) => await launcherV2Callbacks.Types()), new RouteAction( "/launcher/v2/login", - async (url, info, sessionID, _) => await launcherV2Callbacks.Login(info) + async (_, info, _, _) => await launcherV2Callbacks.Login(info) ), new RouteAction( "/launcher/v2/register", - async (url, info, sessionID, _) => await launcherV2Callbacks.Register(info) + async (_, info, _, _) => await launcherV2Callbacks.Register(info) ), new RouteAction( "/launcher/v2/remove", - async (url, info, sessionID, _) => await launcherV2Callbacks.Remove(info) + async (_, info, _, _) => await launcherV2Callbacks.Remove(info) ), new RouteAction( "/launcher/v2/version", - async (url, _, sessionID, _) => await launcherV2Callbacks.CompatibleVersion() + async (_, _, _, _) => await launcherV2Callbacks.CompatibleVersion() ), - new RouteAction("/launcher/v2/mods", async (url, _, sessionID, _) => await launcherV2Callbacks.Mods()), + new RouteAction("/launcher/v2/mods", async (_, _, _, _) => await launcherV2Callbacks.Mods()), new RouteAction( "/launcher/v2/profiles", - async (url, _, sessionID, _) => await launcherV2Callbacks.Profiles() + async (_, _, _, _) => await launcherV2Callbacks.Profiles() ), new RouteAction( "/launcher/v2/profile", - async (url, info, sessionID, _) => await launcherV2Callbacks.Profile(info) + async (_, info, _, _) => await launcherV2Callbacks.Profile(info) ), + new RouteAction( + "/launcher/v2/wipe", + async (_, info, _, _) => await launcherV2Callbacks.Wipe(info) + ) ] - ) { } + ) +{ +} From 3da3ea1098cb7af98bef8c4d227ce45f8f43b396 Mon Sep 17 00:00:00 2001 From: sp-tarkov-bot Date: Tue, 28 Oct 2025 17:03:57 +0000 Subject: [PATCH 07/36] Format Style Fixes --- .../Callbacks/LauncherV2Callbacks.cs | 43 +++++-------------- .../Controllers/LauncherV2Controller.cs | 7 +-- .../Routers/Static/LauncherV2StaticRouter.cs | 39 ++++------------- 3 files changed, 22 insertions(+), 67 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs b/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs index b4b559180..ee990938d 100644 --- a/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs +++ b/Libraries/SPTarkov.Server.Core/Callbacks/LauncherV2Callbacks.cs @@ -16,26 +16,17 @@ ProfileController profileController { public ValueTask Ping() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2PingResponse - { - Response = launcherV2Controller.Ping() - })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2PingResponse { Response = launcherV2Controller.Ping() })); } public ValueTask Types() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2TypesResponse - { - Response = launcherV2Controller.Types() - })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2TypesResponse { Response = launcherV2Controller.Types() })); } public ValueTask Login(LoginRequestData info) { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2LoginResponse - { - Response = launcherV2Controller.Login(info) - })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2LoginResponse { Response = launcherV2Controller.Login(info) })); } public async ValueTask Register(RegisterData info) @@ -65,49 +56,35 @@ public ValueTask Remove(LoginRequestData info) public ValueTask CompatibleVersion() { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2VersionResponse - { - Response = launcherV2Controller.SptVersion() - }) + httpResponseUtil.NoBody(new LauncherV2VersionResponse { Response = launcherV2Controller.SptVersion() }) ); } public ValueTask Mods() { - return new ValueTask(httpResponseUtil.NoBody(new LauncherV2ModsResponse - { - Response = launcherV2Controller.LoadedMods() - })); + return new ValueTask(httpResponseUtil.NoBody(new LauncherV2ModsResponse { Response = launcherV2Controller.LoadedMods() })); } public ValueTask Profiles() { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2ProfilesResponse - { - Response = profileController.GetMiniProfiles() - }) + httpResponseUtil.NoBody(new LauncherV2ProfilesResponse { Response = profileController.GetMiniProfiles() }) ); } public ValueTask Profile(LoginRequestData username) { return new ValueTask( - httpResponseUtil.NoBody(new LauncherV2ProfileResponse - { - Response = launcherV2Controller.GetMiniProfileFromUsername(username) - }) + 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(), - }) + httpResponseUtil.NoBody( + new LauncherV2WipeResponse { Response = launcherV2Controller.Wipe(info), Profiles = profileController.GetMiniProfiles() } + ) ); } } diff --git a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs index 2aba321b6..c9274ee59 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/LauncherV2Controller.cs @@ -185,9 +185,10 @@ public bool Wipe(RegisterData info) if (!sessionId) { - var profileInfo = saveServer.GetProfiles().FirstOrDefault(x => - x.Value.ProfileInfo?.Username == info.Username - ).Value.ProfileInfo; + var profileInfo = saveServer + .GetProfiles() + .FirstOrDefault(x => x.Value.ProfileInfo?.Username == info.Username) + .Value.ProfileInfo; profileInfo!.Edition = info.Edition; profileInfo.IsWiped = true; diff --git a/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs index fd5bce760..95324cb7c 100644 --- a/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs +++ b/Libraries/SPTarkov.Server.Core/Routers/Static/LauncherV2StaticRouter.cs @@ -14,36 +14,13 @@ public class LauncherV2StaticRouter(LauncherV2Callbacks launcherV2Callbacks, Jso [ 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/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) - ) + 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)), ] - ) -{ -} + ) { } From b99f3f67cc4012bd4506887470125e35ce7d55bf Mon Sep 17 00:00:00 2001 From: Cj <161484149+CJ-SPT@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:01:46 -0400 Subject: [PATCH 08/36] Merge pull request #669 from sp-tarkov/Assembly-ref-validation Validate core assembly reference when loading mods --- SPTarkov.Server/Modding/ModValidator.cs | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) 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" /// From 2d6129b2a203889403ec812f5ad98a9b715b79d2 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 17:00:31 +0000 Subject: [PATCH 09/36] removed zombies from customs and interchange + increased infection across other maps that have zombie kill quests --- .../SPT_Data/configs/seasonalevents.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 5d75f300f..7bb0fcbe0 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -9688,12 +9688,12 @@ "disableWaves": [], "enabled": true, "mapInfectionAmount": { - "Sandbox": 5, - "factory4": 5, + "Sandbox": 0, + "factory4": 50, "laboratory": 100, - "Woods": 5, - "bigmap": 5, - "Shoreline": 5, + "Woods": 50, + "bigmap": 0, + "Shoreline": 50, "Interchange": 5, "RezervBase": 5, "Lighthouse": 5, From 15fbe958c91727e9c949db688207b8b49717ec98 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 17:01:57 +0000 Subject: [PATCH 10/36] Don't apply hostility changes to maps without zombies during halloween `ReplaceBotHostiltiy` has optional map whitelist param --- .../Services/SeasonalEventService.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index 6fb35ab15..27999a1ba 100644 --- a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs @@ -457,7 +457,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) @@ -602,7 +605,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; @@ -630,6 +636,11 @@ protected void ReplaceBotHostility(Dictionary From af38ef41a145a2db13a7b2ba03755f3b6dfb78cc Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 17:30:43 +0000 Subject: [PATCH 11/36] Updated hostility values for maps with infection: bosses = hostile to player not to pmc bots followers = hostile to player not to pmc bots pmcs = hostile to player + always hostile to scavs scavs = hostile to player and pmc bots raiders = hostile to player and pmc bots Adjusted infection rates to just maps with zombie kill quests --- .../SPT_Data/configs/seasonalevents.json | 221 +++++++++--------- 1 file changed, 109 insertions(+), 112 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 7bb0fcbe0..6351b859c 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -9688,16 +9688,16 @@ "disableWaves": [], "enabled": true, "mapInfectionAmount": { - "Sandbox": 0, + "Sandbox": 25, "factory4": 50, "laboratory": 100, - "Woods": 50, + "Woods": 60, "bigmap": 0, "Shoreline": 50, - "Interchange": 5, - "RezervBase": 5, - "Lighthouse": 5, - "TarkovStreets": 5 + "Interchange": 0, + "RezervBase": 50, + "Lighthouse": 50, + "TarkovStreets": 50 } } }, @@ -9948,9 +9948,11 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "pmcBEAR", + "pmcUSEC" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "assault", "Neutral": [ "marksman", @@ -10002,12 +10004,10 @@ "spiritWinter", "spiritSpring", "peacemaker", - "pmcBEAR", - "pmcUSEC", "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10017,7 +10017,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossTest", "Neutral": [ "marksman", @@ -10074,7 +10074,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10084,7 +10084,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBully", "Neutral": [ "marksman", @@ -10141,7 +10141,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10218,7 +10218,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBully", "Neutral": [ "marksman", @@ -10275,7 +10275,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10285,7 +10285,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKilla", "Neutral": [ "marksman", @@ -10342,7 +10342,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10352,7 +10352,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKojaniy", "Neutral": [ "marksman", @@ -10409,7 +10409,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10419,7 +10419,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKojaniy", "Neutral": [ "marksman", @@ -10476,7 +10476,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10484,9 +10484,11 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "pmcBEAR", + "pmcUSEC" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "pmcBot", "Neutral": [ "marksman", @@ -10538,12 +10540,10 @@ "spiritWinter", "spiritSpring", "peacemaker", - "pmcBEAR", - "pmcUSEC", "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10620,7 +10620,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossGluhar", "Neutral": [ "marksman", @@ -10677,7 +10677,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10687,7 +10687,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharAssault", "Neutral": [ "marksman", @@ -10744,7 +10744,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10754,7 +10754,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharSecurity", "Neutral": [ "marksman", @@ -10811,7 +10811,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10821,7 +10821,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharScout", "Neutral": [ "marksman", @@ -10878,7 +10878,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10888,7 +10888,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerGluharSnipe", "Neutral": [ "marksman", @@ -10945,7 +10945,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -10955,7 +10955,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerSanitar", "Neutral": [ "marksman", @@ -11012,7 +11012,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11022,7 +11022,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossSanitar", "Neutral": [ "marksman", @@ -11079,7 +11079,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11357,7 +11357,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossTagilla", "Neutral": [ "marksman", @@ -11414,7 +11414,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11424,7 +11424,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerTagilla", "Neutral": [ "marksman", @@ -11481,7 +11481,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11625,7 +11625,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKnight", "Neutral": [ "marksman", @@ -11682,7 +11682,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11692,7 +11692,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBigPipe", "Neutral": [ "marksman", @@ -11749,7 +11749,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11759,7 +11759,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBirdEye", "Neutral": [ "marksman", @@ -11816,7 +11816,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -11960,7 +11960,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBoar", "Neutral": [ "marksman", @@ -12017,7 +12017,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12027,7 +12027,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoar", "Neutral": [ "marksman", @@ -12084,7 +12084,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12228,7 +12228,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossBoarSniper", "Neutral": [ "marksman", @@ -12285,7 +12285,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12563,7 +12563,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoarClose1", "Neutral": [ "marksman", @@ -12620,7 +12620,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12630,7 +12630,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerBoarClose2", "Neutral": [ "marksman", @@ -12687,7 +12687,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12697,7 +12697,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossKolontay", "Neutral": [ "marksman", @@ -12754,7 +12754,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12764,7 +12764,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKolontayAssault", "Neutral": [ "marksman", @@ -12821,7 +12821,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12831,7 +12831,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "followerKolontaySecurity", "Neutral": [ "marksman", @@ -12888,7 +12888,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -12965,7 +12965,7 @@ "infectedLaborant", "infectedTagilla" ], - "BearPlayerBehaviour": "Neutral", + "BearPlayerBehaviour": "AlwaysEnemies", "BotRole": "bossPartisan", "Neutral": [ "marksman", @@ -13022,7 +13022,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecPlayerBehaviour": "Neutral" + "UsecPlayerBehaviour": "AlwaysEnemies" }, { "AlwaysEnemies": [ @@ -13231,9 +13231,10 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" + "infectedTagilla", + "assault" ], - "BearEnemyChance": 15, + "BearEnemyChance": 90, "BearPlayerBehaviour": "ChancedEnemies", "BotRole": "pmcBEAR", "Neutral": [ @@ -13291,7 +13292,7 @@ "skier" ], "SavagePlayerBehaviour": "Neutral", - "UsecEnemyChance": 30, + "UsecEnemyChance": 90, "UsecPlayerBehaviour": "ChancedEnemies" }, { @@ -13300,12 +13301,7 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" - ], - "BearEnemyChance": 30, - "BearPlayerBehaviour": "ChancedEnemies", - "BotRole": "pmcUSEC", - "Neutral": [ + "infectedTagilla", "marksman", "assault", "bossTest", @@ -13315,14 +13311,19 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "cursedAssault", - "bossGluhar", + "bossGluhar", "followerGluharAssault", "followerGluharSecurity", "followerGluharScout", "followerGluharSnipe", "followerSanitar", "bossSanitar", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13330,7 +13331,6 @@ "bossTagilla", "followerTagilla", "exUsec", - "gifter", "bossKnight", "followerBigPipe", "followerBirdEye", @@ -13342,25 +13342,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,12 +13368,8 @@ "infectedPmc", "infectedCivil", "infectedLaborant", - "infectedTagilla" - ], - "BearPlayerBehaviour": "Neutral", - "BotRole": "skier", - "Neutral": [ - "marksman", + "infectedTagilla", + "marksman", "assault", "bossTest", "bossBully", @@ -13383,14 +13378,19 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "cursedAssault", - "bossGluhar", + "bossGluhar", "followerGluharAssault", "followerGluharSecurity", "followerGluharScout", "followerGluharSnipe", "followerSanitar", "bossSanitar", + "bossPartisan", + "spiritWinter", + "spiritSpring", + "peacemaker", + "skier", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13398,7 +13398,6 @@ "bossTagilla", "followerTagilla", "exUsec", - "gifter", "bossKnight", "followerBigPipe", "followerBirdEye", @@ -13410,22 +13409,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" From 069aec97f9004a3118c0962ec60b2d5846b29ba2 Mon Sep 17 00:00:00 2001 From: sp-tarkov-bot Date: Wed, 29 Oct 2025 17:31:41 +0000 Subject: [PATCH 12/36] Format Style Fixes --- .../SPT_Data/configs/seasonalevents.json | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 6351b859c..da024a7a9 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -9949,7 +9949,7 @@ "infectedCivil", "infectedLaborant", "infectedTagilla", - "pmcBEAR", + "pmcBEAR", "pmcUSEC" ], "BearPlayerBehaviour": "AlwaysEnemies", @@ -10485,7 +10485,7 @@ "infectedCivil", "infectedLaborant", "infectedTagilla", - "pmcBEAR", + "pmcBEAR", "pmcUSEC" ], "BearPlayerBehaviour": "AlwaysEnemies", @@ -13311,19 +13311,19 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "bossGluhar", + "bossGluhar", "followerGluharAssault", "followerGluharSecurity", "followerGluharScout", "followerGluharSnipe", "followerSanitar", "bossSanitar", - "bossPartisan", + "bossPartisan", "spiritWinter", "spiritSpring", "peacemaker", "skier", - "cursedAssault", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13354,9 +13354,9 @@ "BearPlayerBehaviour": "ChancedEnemies", "BotRole": "pmcUSEC", "Neutral": [ - "gifter", - "peacefullZryachiyEvent", - "shooterBTR" + "gifter", + "peacefullZryachiyEvent", + "shooterBTR" ], "SavagePlayerBehaviour": "Neutral", "UsecEnemyChance": 90, @@ -13369,7 +13369,7 @@ "infectedCivil", "infectedLaborant", "infectedTagilla", - "marksman", + "marksman", "assault", "bossTest", "bossBully", @@ -13378,19 +13378,19 @@ "bossKojaniy", "followerKojaniy", "pmcBot", - "bossGluhar", + "bossGluhar", "followerGluharAssault", "followerGluharSecurity", "followerGluharScout", "followerGluharSnipe", "followerSanitar", "bossSanitar", - "bossPartisan", + "bossPartisan", "spiritWinter", "spiritSpring", "peacemaker", "skier", - "cursedAssault", + "cursedAssault", "test", "assaultGroup", "sectantWarrior", @@ -13421,8 +13421,8 @@ "BotRole": "skier", "Neutral": [ "gifter", - "peacefullZryachiyEvent", - "shooterBTR" + "peacefullZryachiyEvent", + "shooterBTR" ], "SavagePlayerBehaviour": "Neutral", "UsecPlayerBehaviour": "Neutral" From 02c20520c5b7c4bbc979b41abd0941132e7bd985 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 19:39:07 +0000 Subject: [PATCH 13/36] Added missing values for event bosses --- .../SPT_Data/configs/bot.json | 42 +++++++++++++++++++ .../database/bots/types/sectantoni.json | 16 ++++++- .../bots/types/sectantpredvestnik.json | 16 ++++++- .../database/bots/types/sectantprizrak.json | 16 ++++++- 4 files changed, 84 insertions(+), 6 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json index bad725239..d966ebf66 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json @@ -324,6 +324,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, 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..5af5d57d4 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..6f7531d78 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..f46de92c5 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 }, From a63fe7ce2b081b6d1f7f2f8d4478c1e922fc1e81 Mon Sep 17 00:00:00 2001 From: sp-tarkov-bot Date: Wed, 29 Oct 2025 19:40:09 +0000 Subject: [PATCH 14/36] Format Style Fixes --- .../SPT_Data/database/bots/types/sectantoni.json | 4 ++-- .../SPT_Data/database/bots/types/sectantpredvestnik.json | 4 ++-- .../SPT_Data/database/bots/types/sectantprizrak.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 5af5d57d4..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 @@ -27,11 +27,11 @@ "min": 2100, "max": 2100 }, - "easy": { + "easy": { "min": 2100, "max": 2100 }, - "hard": { + "hard": { "min": 2100, "max": 2100 } 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 6f7531d78..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 @@ -26,11 +26,11 @@ "min": 2300, "max": 2300 }, - "easy": { + "easy": { "min": 2300, "max": 2300 }, - "hard": { + "hard": { "min": 2300, "max": 2300 } 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 f46de92c5..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 @@ -27,11 +27,11 @@ "min": 2100, "max": 2100 }, - "easy": { + "easy": { "min": 2100, "max": 2100 }, - "hard": { + "hard": { "min": 2100, "max": 2100 } From 7763ebc55a04b1f94c63d914d9a7f1bb2ba35d12 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 20:30:47 +0000 Subject: [PATCH 15/36] Added missing values for `ravangezryachiyevent` Fixed preset typo `bossTagillaAgro` --- .../SPT_Data/configs/bot.json | 16 +++++++++++++++- .../database/bots/types/bosstagillaagro.json | 16 ++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json index d966ebf66..8b311d598 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json @@ -295,6 +295,20 @@ "minDelta": 20, "minLimitPercent": 15 } + }, + "ravangezryachiyevent": { + "armor": { + "maxDelta": 10, + "minDelta": 0, + "minLimitPercent": 15 + }, + "weapon": { + "highestMax": 100, + "lowestMax": 80, + "maxDelta": 40, + "minDelta": 20, + "minLimitPercent": 15 + } }, "sectantpriest": { "armor": { @@ -2800,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/database/bots/types/bosstagillaagro.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/database/bots/types/bosstagillaagro.json index 79dbb8b9f..574a5f469 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 }, From 1d73bbee642686acaddc4341e88209e9a6b47330 Mon Sep 17 00:00:00 2001 From: sp-tarkov-bot Date: Wed, 29 Oct 2025 20:31:44 +0000 Subject: [PATCH 16/36] Format Style Fixes --- Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json | 2 +- .../SPT_Data/database/bots/types/bosstagillaagro.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json index 8b311d598..2218ec0b8 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/bot.json @@ -296,7 +296,7 @@ "minLimitPercent": 15 } }, - "ravangezryachiyevent": { + "ravangezryachiyevent": { "armor": { "maxDelta": 10, "minDelta": 0, 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 574a5f469..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 @@ -2137,11 +2137,11 @@ "max": 6666, "min": 6666 }, - "easy": { + "easy": { "max": 6666, "min": 6666 }, - "hard": { + "hard": { "max": 6666, "min": 6666 } From 60a2891901390ac32abb6c177f9732763e4d5f94 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 21:33:17 +0000 Subject: [PATCH 17/36] Flagged `Night of The Cult` as halloween quest --- Libraries/SPTarkov.Server.Assets/SPT_Data/configs/quest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 2aebb3643bb296aae46b7255bf322d018f6e6fa2 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 23:19:37 +0000 Subject: [PATCH 18/36] Fixed incorrect logic --- Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index 27999a1ba..675e5314a 100644 --- a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs @@ -898,7 +898,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; } From b6b67e0ecc7aab94baa2f218805b595260f438bd Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 29 Oct 2025 23:23:24 +0000 Subject: [PATCH 19/36] Enabled `Night of The Cult` bosses to spawn --- .../SPT_Data/configs/seasonalevents.json | 133 +++++++++++++++++- .../Services/SeasonalEventService.cs | 4 +- 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index da024a7a9..1388ce3ae 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -9395,7 +9395,138 @@ "TriggerName": "" } ] - } + }, + "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", diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index 675e5314a..9cff7b1fb 100644 --- a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs @@ -480,6 +480,8 @@ protected void ApplyHalloweenEvent(SeasonalEvent eventType, Config globalConfig) { usec.BotAppearance.Head[new MongoId("6644d2da35d958070c02642c")] = 30; } + + AddEventBossesToMaps("halloweennightcult"); } protected void ApplyChristmasEvent(SeasonalEvent eventType, Config globalConfig) @@ -909,7 +911,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); } } From 03147425f9d0bfdf26c45fc608176e73c623e6fd Mon Sep 17 00:00:00 2001 From: sp-tarkov-bot Date: Wed, 29 Oct 2025 23:24:23 +0000 Subject: [PATCH 20/36] Format Style Fixes --- .../SPT_Data/configs/seasonalevents.json | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 1388ce3ae..9fb8e4dcb 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json @@ -9396,95 +9396,95 @@ } ] }, - "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": [ - { + "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", @@ -9525,8 +9525,8 @@ "TriggerId": "", "TriggerName": "" } - ] - } + ] + } }, "eventBotMapping": { "arenaFighterEvent": "arenaFighterEvent", From ef18c902dcaccfd122c36165a1ae49dc8ae9ccde Mon Sep 17 00:00:00 2001 From: DrakiaXYZ <565558+DrakiaXYZ@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:05:34 -0700 Subject: [PATCH 21/36] Addd a new ReleaseCheckService to notify users of updates (#670) * Addd a new ReleaseCheckService to notify users of updates - Pulls the latest release from GitHub API to compare the tag against the users current SPT version - Runs at the very end of the startup process to avoid being pushed off screen by mod logging - Only notifies of patch version increments, not major or minor increments - Links the release notes so users can Ctrl+Click to open directly to the upgrade page - Is run on its own thread, and discards all errors, so as to not impact users without an internet connection or ability to access GitHub * Formatting * Use record for the ReleaseInformation class --------- Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> --- .../Services/ReleaseCheckService.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Libraries/SPTarkov.Server.Core/Services/ReleaseCheckService.cs 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; } + } +} From df068fb8bdf65920ca094e307f839191cb815004 Mon Sep 17 00:00:00 2001 From: Chomp Date: Thu, 30 Oct 2025 10:45:22 +0000 Subject: [PATCH 22/36] ProfileDataService changes: Added `ClearProfileData()` Replaced filepath access with `Path.Combine` Reduced various sources of duplication --- .../Controllers/LauncherController.cs | 5 ++ .../Services/Mod/ProfileDataService.cs | 57 +++++++++++++++---- 2 files changed, 52 insertions(+), 10 deletions(-) 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/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}"; } } From 1b07ce530d0df816ba593ba23f1c08a4a87514fd Mon Sep 17 00:00:00 2001 From: Chomp Date: Thu, 30 Oct 2025 13:34:20 +0000 Subject: [PATCH 23/36] Adjusted `Goons` spawn chance to 20% across `Customs/Lighthouse/Woods/Shoreline` --- .../SPT_Data/database/locations/bigmap/base.json | 2 +- .../SPT_Data/database/locations/lighthouse/base.json | 2 +- .../SPT_Data/database/locations/shoreline/base.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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", From cac81f417d1b7060ea7b4a02537758d123be0945 Mon Sep 17 00:00:00 2001 From: Tyfon <29051038+tyfon7@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:03:24 -0700 Subject: [PATCH 24/36] Account for compound items in DialogHelper.GetMessageItemContents --- Libraries/SPTarkov.Server.Core/Helpers/DialogueHelper.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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; From ab882617d7b5487e278a6b0241a8e4ed8689eb21 Mon Sep 17 00:00:00 2001 From: Chomp Date: Thu, 30 Oct 2025 22:13:53 +0000 Subject: [PATCH 25/36] Generate weapon/armor price based on the child item price total --- .../SPT_Data/configs/ragfair.json | 1 + .../Generators/RagfairOfferGenerator.cs | 3 +- .../Models/Spt/Config/RagfairConfig.cs | 6 ++++ .../Services/RagfairPriceService.cs | 30 +++++++++++++++++-- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json index 4f92c947d..a57c82378 100644 --- a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json +++ b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json @@ -346,6 +346,7 @@ "useTraderPriceForOffersIfHigher": true, "generateBaseFleaPrices": { "useHandbookPrice": true, + "generatePresetPriceByChildren": true, "priceMultiplier": 1.5, "itemTplMultiplierOverride": { "5780cf7f2459777de4559322": 1.8 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/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/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 /// From 4070f866fa7310a56899f201e4bb83dd303b6595 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 13:45:06 +0000 Subject: [PATCH 26/36] Added halloween event bosses to april event --- Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs b/Libraries/SPTarkov.Server.Core/Services/SeasonalEventService.cs index 9cff7b1fb..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)) { From e3114ae23f475cb56e8a4bb26f16b49991e1bf88 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 15:02:30 +0000 Subject: [PATCH 27/36] Flagged infected spawns as `ForceSpawn` and `` --- .../SPT_Data/configs/seasonalevents.json | 490 +++++++++--------- 1 file changed, 245 insertions(+), 245 deletions(-) diff --git a/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/seasonalevents.json index 9fb8e4dcb..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": [ From ae15dc1beef1b4b61590d16a379c9c859cca10b2 Mon Sep 17 00:00:00 2001 From: Archangel Date: Fri, 31 Oct 2025 16:53:08 +0100 Subject: [PATCH 28/36] Add migration for invalid pockets --- .../Migrations/Fixes/InvalidPocketFix.cs | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 Libraries/SPTarkov.Server.Core/Migration/Migrations/Fixes/InvalidPocketFix.cs 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); + } +} From e5bb62cf62e31d2aab9e34c1dcac70d1c941e014 Mon Sep 17 00:00:00 2001 From: Archangel Date: Fri, 31 Oct 2025 17:05:13 +0100 Subject: [PATCH 29/36] Default assign IEnumerable --- .../SPTarkov.Server.Core/Migration/AbstractProfileMigration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From 421ab55c23e0d75c29d3785ffd14a5d729e5e093 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 16:29:55 +0000 Subject: [PATCH 30/36] Post raid effect fixes: When exiting raid with severe muscle pain, prevent client instructing server to add mild muscle pain When exiting a raid with effect that has a timer, decrease timer value by amount of time spent in raid --- .../Helpers/HealthHelper.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) 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; } From f6d9d461a688b8f4f88cff085ce024f5bae10270 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 16:36:46 +0000 Subject: [PATCH 31/36] Updated nuget packages --- Libraries/SPTarkov.DI/SPTarkov.DI.csproj | 2 +- .../SPTarkov.Server.Core/SPTarkov.Server.Core.csproj | 2 +- SPTarkov.Server/SPTarkov.Server.csproj | 8 ++++---- Testing/Benchmarks/Benchmarks.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Libraries/SPTarkov.DI/SPTarkov.DI.csproj b/Libraries/SPTarkov.DI/SPTarkov.DI.csproj index bc264fd01..dd45bfeab 100644 --- a/Libraries/SPTarkov.DI/SPTarkov.DI.csproj +++ b/Libraries/SPTarkov.DI/SPTarkov.DI.csproj @@ -16,7 +16,7 @@ - + diff --git a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj index 50d59c2bb..695e61175 100644 --- a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj +++ b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj @@ -20,7 +20,7 @@ - + diff --git a/SPTarkov.Server/SPTarkov.Server.csproj b/SPTarkov.Server/SPTarkov.Server.csproj index 8555185c4..49aa59a03 100644 --- a/SPTarkov.Server/SPTarkov.Server.csproj +++ b/SPTarkov.Server/SPTarkov.Server.csproj @@ -32,10 +32,10 @@ - - - - + + + + diff --git a/Testing/Benchmarks/Benchmarks.csproj b/Testing/Benchmarks/Benchmarks.csproj index 4af4884ae..49de5b68b 100644 --- a/Testing/Benchmarks/Benchmarks.csproj +++ b/Testing/Benchmarks/Benchmarks.csproj @@ -5,7 +5,7 @@ enable - + From b92aedbc0c573e1e4fb2813e5816862552d846be Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 18:24:30 +0000 Subject: [PATCH 32/36] Fixed player scav not having correct HP values on limbs #642 --- .../Generators/BotGenerator.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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 /// From 73d299830a9cac2764a7a62ac7456c6fc0785ad5 Mon Sep 17 00:00:00 2001 From: Archangel Date: Fri, 31 Oct 2025 19:26:30 +0100 Subject: [PATCH 33/36] Remove unused record --- .../Models/Eft/Common/Tables/BotCore.cs | 399 ------------------ 1 file changed, 399 deletions(-) delete mode 100644 Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/BotCore.cs 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; } -} From 2be1310c64263d508d3388ad41cdb5f0681c3045 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 18:37:02 +0000 Subject: [PATCH 34/36] Revert "Updated nuget packages" This reverts commit f6d9d461a688b8f4f88cff085ce024f5bae10270. --- Libraries/SPTarkov.DI/SPTarkov.DI.csproj | 2 +- .../SPTarkov.Server.Core/SPTarkov.Server.Core.csproj | 2 +- SPTarkov.Server/SPTarkov.Server.csproj | 8 ++++---- Testing/Benchmarks/Benchmarks.csproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Libraries/SPTarkov.DI/SPTarkov.DI.csproj b/Libraries/SPTarkov.DI/SPTarkov.DI.csproj index dd45bfeab..bc264fd01 100644 --- a/Libraries/SPTarkov.DI/SPTarkov.DI.csproj +++ b/Libraries/SPTarkov.DI/SPTarkov.DI.csproj @@ -16,7 +16,7 @@ - + diff --git a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj index 695e61175..50d59c2bb 100644 --- a/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj +++ b/Libraries/SPTarkov.Server.Core/SPTarkov.Server.Core.csproj @@ -20,7 +20,7 @@ - + diff --git a/SPTarkov.Server/SPTarkov.Server.csproj b/SPTarkov.Server/SPTarkov.Server.csproj index 49aa59a03..8555185c4 100644 --- a/SPTarkov.Server/SPTarkov.Server.csproj +++ b/SPTarkov.Server/SPTarkov.Server.csproj @@ -32,10 +32,10 @@ - - - - + + + + diff --git a/Testing/Benchmarks/Benchmarks.csproj b/Testing/Benchmarks/Benchmarks.csproj index 49de5b68b..4af4884ae 100644 --- a/Testing/Benchmarks/Benchmarks.csproj +++ b/Testing/Benchmarks/Benchmarks.csproj @@ -5,7 +5,7 @@ enable - + From 91c9abd6543bb96b472fe138ba88356181c477ce Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 18:43:06 +0000 Subject: [PATCH 35/36] Added `IMP mine detector` to reward and flea blacklist --- Libraries/SPTarkov.Server.Assets/SPT_Data/configs/item.json | 3 ++- Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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/ragfair.json b/Libraries/SPTarkov.Server.Assets/SPT_Data/configs/ragfair.json index a57c82378..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, From ac88c05e1e8e63eab8ddbc4b7160573a3e228193 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 31 Oct 2025 18:55:47 +0000 Subject: [PATCH 36/36] Fixed weapon builds not overwriting existing #654 Cleaned up `SaveWeaponBuild` and `SaveEquipmentBuild` --- .../Controllers/BuildController.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 {