Expanded max loot in pmc container systems
Now allows for per-map multipliers + pocket + vest loot uses same level-based value as backpack labs multiplier = 2 labyrinth = 4 Removed nullability from some properties
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -418,6 +418,7 @@ public class BotController(
|
||||
LocationSpecificPmcLevelOverride = GetPmcLevelRangeForMap(raidSettings?.Location), // Min/max levels for PMCs to generate within
|
||||
IsPlayerScav = false,
|
||||
AllPmcsHaveSameNameAsPlayer = allPmcsHaveSameNameAsPlayer,
|
||||
Location = raidSettings?.Location,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using SPTarkov.Server.Core.Models.Spt.Config;
|
||||
|
||||
namespace SPTarkov.Server.Core.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the rouble amount for the desired container, multiplied by the current map bot will spawn on
|
||||
/// </summary>
|
||||
public static class LootContainerSettingsExtensions
|
||||
{
|
||||
public static double GetRoubleValue(
|
||||
this LootContainerSettings settings,
|
||||
int botLevel,
|
||||
string locationId
|
||||
)
|
||||
{
|
||||
var roubleTotalByLevel = GetContainerRoubleTotalByLevel(
|
||||
botLevel,
|
||||
settings.TotalRubByLevel
|
||||
);
|
||||
|
||||
// Get multiplier for map, use default if map not found
|
||||
if (!settings.LocationMultipler.TryGetValue(locationId, out var multiplier))
|
||||
{
|
||||
settings.LocationMultipler.TryGetValue("default", out multiplier);
|
||||
}
|
||||
|
||||
return roubleTotalByLevel * multiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rouble cost total for loot in a bots backpack by the bots level
|
||||
/// Will return 0 for non PMCs
|
||||
/// </summary>
|
||||
/// <param name="botLevel">level of the bot</param>
|
||||
/// <param name="containerLootValuesPool">Pocket/vest/backpack</param>
|
||||
/// <returns>rouble amount</returns>
|
||||
private static double GetContainerRoubleTotalByLevel(
|
||||
int botLevel,
|
||||
List<MinMaxLootValue> containerLootValuesPool
|
||||
)
|
||||
{
|
||||
var matchingValue = containerLootValuesPool.FirstOrDefault(minMaxValue =>
|
||||
botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
|
||||
);
|
||||
|
||||
if (matchingValue is null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return matchingValue.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,10 +131,9 @@ public class BotGenerator(
|
||||
);
|
||||
|
||||
// Get raw json data for bot (Cloned)
|
||||
var botRole =
|
||||
botGenerationDetails.IsPmc ?? false
|
||||
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
|
||||
: botGenerationDetails.Role;
|
||||
var botRole = botGenerationDetails.IsPmc
|
||||
? preparedBotBase.Info.Side // Use side to get usec.json or bear.json when bot will be PMC
|
||||
: botGenerationDetails.Role;
|
||||
var botJsonTemplateClone = cloner.Clone(botHelper.GetBotTemplate(botRole));
|
||||
if (botJsonTemplateClone is null)
|
||||
{
|
||||
@@ -195,7 +194,7 @@ public class BotGenerator(
|
||||
);
|
||||
|
||||
// Only filter bot equipment, never players
|
||||
if (!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false))
|
||||
if (!botGenerationDetails.IsPlayerScav)
|
||||
{
|
||||
botEquipmentFilterService.FilterBotEquipment(
|
||||
sessionId,
|
||||
@@ -213,15 +212,12 @@ public class BotGenerator(
|
||||
);
|
||||
|
||||
// Only Pmcs should have a lower nickname
|
||||
bot.Info.LowerNickname = botGenerationDetails.IsPmc.GetValueOrDefault(false)
|
||||
bot.Info.LowerNickname = botGenerationDetails.IsPmc
|
||||
? bot.Info.Nickname.ToLowerInvariant()
|
||||
: string.Empty;
|
||||
|
||||
// Only run when generating a 'fake' playerscav, not actual player scav
|
||||
if (
|
||||
!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
|
||||
&& ShouldSimulatePlayerScav(botRoleLowercase)
|
||||
)
|
||||
if (!botGenerationDetails.IsPlayerScav && ShouldSimulatePlayerScav(botRoleLowercase))
|
||||
{
|
||||
botNameService.AddRandomPmcNameToBotMainProfileNicknameProperty(bot);
|
||||
SetRandomisedGameVersionAndCategory(bot.Info);
|
||||
@@ -242,12 +238,7 @@ public class BotGenerator(
|
||||
RemoveBlacklistedLootFromBotTemplate(botJsonTemplate.BotInventory);
|
||||
|
||||
// Remove hideout data if bot is not a PMC or pscav - match what live sends
|
||||
if (
|
||||
!(
|
||||
botGenerationDetails.IsPmc.GetValueOrDefault(false)
|
||||
|| botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
|
||||
)
|
||||
)
|
||||
if (!(botGenerationDetails.IsPmc || botGenerationDetails.IsPlayerScav))
|
||||
{
|
||||
bot.Hideout = null;
|
||||
}
|
||||
@@ -280,14 +271,11 @@ public class BotGenerator(
|
||||
customisation.Value.Name.Equals(chosenVoiceName, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
.Key;
|
||||
bot.Health = GenerateHealth(
|
||||
botJsonTemplate.BotHealth,
|
||||
botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
|
||||
);
|
||||
bot.Health = GenerateHealth(botJsonTemplate.BotHealth, botGenerationDetails.IsPlayerScav);
|
||||
bot.Skills = GenerateSkills(botJsonTemplate.BotSkills);
|
||||
bot.Info.PrestigeLevel = 0;
|
||||
|
||||
if (botGenerationDetails.IsPmc.GetValueOrDefault(false))
|
||||
if (botGenerationDetails.IsPmc)
|
||||
{
|
||||
bot.Info.IsStreamerModeAvailable = true; // Set to true so client patches can pick it up later - client sometimes alters botrole to assaultGroup
|
||||
SetRandomisedGameVersionAndCategory(bot.Info);
|
||||
@@ -304,7 +292,7 @@ public class BotGenerator(
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
botRoleLowercase,
|
||||
botGenerationDetails.IsPmc.GetValueOrDefault(false),
|
||||
botGenerationDetails,
|
||||
bot.Info.Level.Value,
|
||||
bot.Info.GameVersion
|
||||
);
|
||||
@@ -764,9 +752,7 @@ public class BotGenerator(
|
||||
public void AddIdsToBot(BotBase bot, BotGenerationDetails botGenerationDetails)
|
||||
{
|
||||
bot.Id = new MongoId();
|
||||
bot.Aid = botGenerationDetails.IsPmc.GetValueOrDefault(false)
|
||||
? hashUtil.GenerateAccountId()
|
||||
: 0;
|
||||
bot.Aid = botGenerationDetails.IsPmc ? hashUtil.GenerateAccountId() : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -63,7 +63,7 @@ public class BotInventoryGenerator(
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="botJsonTemplate">Base json db file for the bot having its loot generated</param>
|
||||
/// <param name="botRole">Role bot has (assault/pmcBot)</param>
|
||||
/// <param name="isPmc">Is bot being converted into a pmc</param>
|
||||
/// <param name="botGenerationDetails">Details related to generating a bot</param>
|
||||
/// <param name="botLevel">Level of bot being generated</param>
|
||||
/// <param name="chosenGameVersion">Game version for bot, only really applies for PMCs</param>
|
||||
/// <returns>PmcInventory object with equipment/weapons/loot</returns>
|
||||
@@ -71,7 +71,7 @@ public class BotInventoryGenerator(
|
||||
MongoId sessionId,
|
||||
BotType botJsonTemplate,
|
||||
string botRole,
|
||||
bool isPmc,
|
||||
BotGenerationDetails botGenerationDetails,
|
||||
int botLevel,
|
||||
string chosenGameVersion
|
||||
)
|
||||
@@ -80,6 +80,8 @@ public class BotInventoryGenerator(
|
||||
var wornItemChances = botJsonTemplate.BotChances;
|
||||
var itemGenerationLimitsMinMax = botJsonTemplate.BotGeneration;
|
||||
|
||||
var isPmc = botGenerationDetails.IsPmc;
|
||||
|
||||
// Generate base inventory with no items
|
||||
var botInventory = GenerateInventoryBase();
|
||||
|
||||
@@ -116,6 +118,7 @@ public class BotInventoryGenerator(
|
||||
botLootGenerator.GenerateLoot(
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
botGenerationDetails,
|
||||
isPmc,
|
||||
botRole,
|
||||
botInventory,
|
||||
|
||||
@@ -30,7 +30,7 @@ public class BotLevelGenerator(
|
||||
BotBase bot
|
||||
)
|
||||
{
|
||||
if (!botGenerationDetails.IsPmc.GetValueOrDefault(false))
|
||||
if (!botGenerationDetails.IsPmc)
|
||||
{
|
||||
return new RandomisedBotLevelResult { Exp = 0, Level = 1 };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using SPTarkov.DI.Annotations;
|
||||
using SPTarkov.Server.Core.Extensions;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Common;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
@@ -62,6 +63,7 @@ public class BotLootGenerator(
|
||||
/// </summary>
|
||||
/// <param name="sessionId">Session id</param>
|
||||
/// <param name="botJsonTemplate">Clone of Base JSON db file for the bot having its loot generated</param>
|
||||
/// <param name="botGenerationDetails">Details relating to generating a bot</param>
|
||||
/// <param name="isPmc">Will bot be a pmc</param>
|
||||
/// <param name="botRole">Role of bot, e.g. assault</param>
|
||||
/// <param name="botInventory">Inventory to add loot to</param>
|
||||
@@ -69,6 +71,7 @@ public class BotLootGenerator(
|
||||
public void GenerateLoot(
|
||||
MongoId sessionId,
|
||||
BotType botJsonTemplate,
|
||||
BotGenerationDetails botGenerationDetails,
|
||||
bool isPmc,
|
||||
string botRole,
|
||||
BotBaseInventory botInventory,
|
||||
@@ -300,7 +303,13 @@ public class BotLootGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
var backpackLootRoubleTotal = GetBackpackRoubleTotalByLevel(botLevel, isPmc);
|
||||
var backpackLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Backpack.GetRoubleValue(
|
||||
botLevel,
|
||||
botGenerationDetails.Location
|
||||
)
|
||||
: 0;
|
||||
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
botRole,
|
||||
@@ -314,12 +323,16 @@ public class BotLootGenerator(
|
||||
botInventory,
|
||||
botRole,
|
||||
botItemLimits,
|
||||
backpackLootRoubleTotal ?? 0,
|
||||
backpackLootRoubleTotal,
|
||||
isPmc,
|
||||
filledContainerIds
|
||||
);
|
||||
}
|
||||
|
||||
var vestLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Vest.GetRoubleValue(botLevel, botGenerationDetails.Location)
|
||||
: 0;
|
||||
|
||||
// TacticalVest - generate loot if they have one
|
||||
if (containersBotHasAvailable.Contains(EquipmentSlots.TacticalVest))
|
||||
// Vest
|
||||
@@ -337,12 +350,16 @@ public class BotLootGenerator(
|
||||
botInventory,
|
||||
botRole,
|
||||
botItemLimits,
|
||||
_pmcConfig.MaxVestLootTotalRub,
|
||||
vestLootRoubleTotal,
|
||||
isPmc,
|
||||
filledContainerIds
|
||||
);
|
||||
}
|
||||
|
||||
var pocketLootRoubleTotal = isPmc
|
||||
? _pmcConfig.LootSettings.Pocket.GetRoubleValue(botLevel, botGenerationDetails.Location)
|
||||
: 0;
|
||||
|
||||
// Pockets
|
||||
AddLootFromPool(
|
||||
botLootCacheService.GetLootFromCache(
|
||||
@@ -357,7 +374,7 @@ public class BotLootGenerator(
|
||||
botInventory,
|
||||
botRole,
|
||||
botItemLimits,
|
||||
_pmcConfig.MaxPocketLootTotalRub,
|
||||
pocketLootRoubleTotal,
|
||||
isPmc,
|
||||
filledContainerIds
|
||||
);
|
||||
@@ -401,26 +418,6 @@ public class BotLootGenerator(
|
||||
return matchingValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rouble cost total for loot in a bots backpack by the bots levl
|
||||
/// Will return 0 for non PMCs
|
||||
/// </summary>
|
||||
/// <param name="botLevel">Bots level</param>
|
||||
/// <param name="isPmc">Is the bot a PMC</param>
|
||||
/// <returns>int</returns>
|
||||
protected double? GetBackpackRoubleTotalByLevel(int botLevel, bool isPmc)
|
||||
{
|
||||
if (!isPmc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var matchingValue = _pmcConfig.MaxBackpackLootTotalRub.FirstOrDefault(minMaxValue =>
|
||||
botLevel >= minMaxValue.Min && botLevel <= minMaxValue.Max
|
||||
);
|
||||
return matchingValue?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an array of the containers a bot has on them (pockets/backpack/vest)
|
||||
/// </summary>
|
||||
|
||||
@@ -12,19 +12,19 @@ public record BotGenerationDetails
|
||||
/// Should the bot be generated as a PMC
|
||||
/// </summary>
|
||||
[JsonPropertyName("isPmc")]
|
||||
public bool? IsPmc { get; set; }
|
||||
public bool IsPmc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// assault/pmcBot etc
|
||||
/// </summary>
|
||||
[JsonPropertyName("role")]
|
||||
public string? Role { get; set; }
|
||||
public string Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Side of bot
|
||||
/// </summary>
|
||||
[JsonPropertyName("side")]
|
||||
public string? Side { get; set; }
|
||||
public string Side { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Active players current level
|
||||
@@ -57,7 +57,7 @@ public record BotGenerationDetails
|
||||
/// How many to create and store
|
||||
/// </summary>
|
||||
[JsonPropertyName("botCountToGenerate")]
|
||||
public int? BotCountToGenerate { get; set; }
|
||||
public int BotCountToGenerate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Desired difficulty of the bot
|
||||
@@ -69,11 +69,16 @@ public record BotGenerationDetails
|
||||
/// Will the generated bot be a player scav
|
||||
/// </summary>
|
||||
[JsonPropertyName("isPlayerScav")]
|
||||
public bool? IsPlayerScav { get; set; }
|
||||
public bool IsPlayerScav { get; set; }
|
||||
|
||||
[JsonPropertyName("eventRole")]
|
||||
public string? EventRole { get; set; }
|
||||
|
||||
[JsonPropertyName("allPmcsHaveSameNameAsPlayer")]
|
||||
public bool? AllPmcsHaveSameNameAsPlayer { get; set; }
|
||||
public bool AllPmcsHaveSameNameAsPlayer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Map bots will be spawned on
|
||||
/// </summary>
|
||||
public string? Location { get; set; }
|
||||
}
|
||||
|
||||
@@ -106,14 +106,8 @@ public record PmcConfig : BaseConfig
|
||||
Dictionary<string, Dictionary<string, double>>
|
||||
> PmcType { get; set; }
|
||||
|
||||
[JsonPropertyName("maxBackpackLootTotalRub")]
|
||||
public required List<MinMaxLootValue> MaxBackpackLootTotalRub { get; set; }
|
||||
|
||||
[JsonPropertyName("maxPocketLootTotalRub")]
|
||||
public required int MaxPocketLootTotalRub { get; set; }
|
||||
|
||||
[JsonPropertyName("maxVestLootTotalRub")]
|
||||
public required int MaxVestLootTotalRub { get; set; }
|
||||
[JsonPropertyName("lootSettings")]
|
||||
public required PmcLootSettings LootSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many levels above player level can a PMC be
|
||||
@@ -158,6 +152,27 @@ public record PmcConfig : BaseConfig
|
||||
public required Dictionary<string, List<BossLocationSpawn>> CustomPmcWaves { get; set; }
|
||||
}
|
||||
|
||||
public record PmcLootSettings
|
||||
{
|
||||
[JsonPropertyName("pocket")]
|
||||
public LootContainerSettings Pocket { get; set; }
|
||||
|
||||
[JsonPropertyName("vest")]
|
||||
public LootContainerSettings Vest { get; set; }
|
||||
|
||||
[JsonPropertyName("backpack")]
|
||||
public LootContainerSettings Backpack { get; set; }
|
||||
}
|
||||
|
||||
public record LootContainerSettings
|
||||
{
|
||||
[JsonPropertyName("totalRubByLevel")]
|
||||
public List<MinMaxLootValue> TotalRubByLevel { get; set; }
|
||||
|
||||
[JsonPropertyName("locationMultipler")]
|
||||
public Dictionary<string, double> LocationMultipler { get; set; }
|
||||
}
|
||||
|
||||
public record HostilitySettings
|
||||
{
|
||||
[JsonExtensionData]
|
||||
|
||||
@@ -41,7 +41,7 @@ public class BotEquipmentFilterService(
|
||||
{
|
||||
var pmcProfile = profileHelper.GetPmcProfile(sessionId);
|
||||
|
||||
var botRole = botGenerationDetails.IsPmc ?? false ? "pmc" : botGenerationDetails.Role;
|
||||
var botRole = botGenerationDetails.IsPmc ? "pmc" : botGenerationDetails.Role;
|
||||
var botEquipmentBlacklist = GetBotEquipmentBlacklist(botRole, botLevel);
|
||||
var botEquipmentWhitelist = GetBotEquipmentWhitelist(botRole, botLevel);
|
||||
var botWeightingAdjustments = GetBotWeightingAdjustments(botRole, botLevel);
|
||||
|
||||
@@ -54,15 +54,14 @@ public class BotNameService(
|
||||
|
||||
// Never show for players
|
||||
var showTypeInNickname =
|
||||
!botGenerationDetails.IsPlayerScav.GetValueOrDefault(false)
|
||||
&& _botConfig.ShowTypeInNickname;
|
||||
!botGenerationDetails.IsPlayerScav && _botConfig.ShowTypeInNickname;
|
||||
var roleShouldBeUnique = uniqueRoles?.Contains(botRole.ToLowerInvariant());
|
||||
|
||||
var attempts = 0;
|
||||
while (attempts <= 5)
|
||||
{
|
||||
// Get bot name with leading/trailing whitespace removed
|
||||
var name = isPmc.GetValueOrDefault(false) // Explicit handling of PMCs, all other bots will get "first_name last_name"
|
||||
var name = isPmc // Explicit handling of PMCs, all other bots will get "first_name last_name"
|
||||
? botHelper.GetPmcNicknameOfMaxLength(
|
||||
_botConfig.BotNameLengthLimit,
|
||||
botGenerationDetails.Side
|
||||
@@ -78,10 +77,7 @@ public class BotNameService(
|
||||
}
|
||||
|
||||
// Replace pmc bot names with player name + prefix
|
||||
if (
|
||||
botGenerationDetails.IsPmc.GetValueOrDefault(false)
|
||||
&& botGenerationDetails.AllPmcsHaveSameNameAsPlayer.GetValueOrDefault(false)
|
||||
)
|
||||
if (botGenerationDetails.IsPmc && botGenerationDetails.AllPmcsHaveSameNameAsPlayer)
|
||||
{
|
||||
var prefix = serverLocalisationService.GetRandomTextThatMatchesPartialKey(
|
||||
"pmc-name_prefix_"
|
||||
|
||||
Reference in New Issue
Block a user