diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
index d14809fc..cd921be3 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs
@@ -1,6 +1,7 @@
using System.Collections.Frozen;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Constants;
+using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums;
@@ -47,19 +48,21 @@ public class BotGeneratorHelper(
///
/// Item extra properties are being generated for
/// Used by weapons to randomize the durability values. Null for non-equipped items
+ /// Force property on item
/// Item Upd object with extra properties
- public Upd GenerateExtraPropertiesForItem(TemplateItem? itemTemplate, string? botRole = null)
+ public Upd? GenerateExtraPropertiesForItem(TemplateItem? itemTemplate, string? botRole = null, bool forceStackObjectsCount = false)
{
// Get raid settings, if no raid, default to day
var raidSettings = profileActivityService.GetFirstProfileActivityRaidData()?.RaidConfiguration;
+ // BotRole property exists, we have specific bot randomisation values to make use of
RandomisedResourceDetails? randomisationSettings = null;
if (botRole is not null)
{
BotConfig.LootItemResourceRandomization.TryGetValue(botRole, out randomisationSettings);
}
- Upd itemProperties = new();
+ Upd itemUpd = new();
var hasProperties = false;
if (itemTemplate?.Properties?.MaxDurability is not null && itemTemplate.Properties.MaxDurability > 0)
@@ -67,32 +70,32 @@ public class BotGeneratorHelper(
if (itemTemplate.Properties.WeapClass is not null)
{
// Is weapon
- itemProperties.Repairable = GenerateWeaponRepairableProperties(itemTemplate, botRole);
+ itemUpd.Repairable = GenerateWeaponRepairableProperties(itemTemplate, botRole);
hasProperties = true;
}
else if (itemTemplate.Properties.ArmorClass is not null)
{
// Is armor
- itemProperties.Repairable = GenerateArmorRepairableProperties(itemTemplate, botRole);
+ itemUpd.Repairable = GenerateArmorRepairableProperties(itemTemplate, botRole);
hasProperties = true;
}
}
if (itemTemplate?.Properties?.HasHinge ?? false)
{
- itemProperties.Togglable = new UpdTogglable { On = true };
+ itemUpd.Togglable = new UpdTogglable { On = true };
hasProperties = true;
}
if (itemTemplate?.Properties?.Foldable ?? false)
{
- itemProperties.Foldable = new UpdFoldable { Folded = false };
+ itemUpd.Foldable = new UpdFoldable { Folded = false };
hasProperties = true;
}
if (itemTemplate?.Properties?.WeapFireType?.Count == 0)
{
- itemProperties.FireMode = itemTemplate.Properties.WeapFireType.Contains("fullauto")
+ itemUpd.FireMode = itemTemplate.Properties.WeapFireType.Contains("fullauto")
? new UpdFireMode { FireMode = "fullauto" }
: new UpdFireMode { FireMode = randomUtil.GetArrayValue(itemTemplate.Properties.WeapFireType) };
hasProperties = true;
@@ -100,7 +103,7 @@ public class BotGeneratorHelper(
if (itemTemplate?.Properties?.MaxHpResource is not null)
{
- itemProperties.MedKit = new UpdMedKit
+ itemUpd.MedKit = new UpdMedKit
{
HpResource = GetRandomizedResourceValue(itemTemplate.Properties.MaxHpResource ?? 0, randomisationSettings?.Meds),
};
@@ -109,7 +112,7 @@ public class BotGeneratorHelper(
if (itemTemplate?.Properties?.MaxResource is not null && itemTemplate.Properties?.FoodUseTime is not null)
{
- itemProperties.FoodDrink = new UpdFoodDrink
+ itemUpd.FoodDrink = new UpdFoodDrink
{
HpPercent = GetRandomizedResourceValue(itemTemplate.Properties.MaxResource ?? 0, randomisationSettings?.Food),
};
@@ -124,7 +127,7 @@ public class BotGeneratorHelper(
? equipmentSettings?.LightIsActiveNightChancePercent ?? 50
: equipmentSettings?.LightIsActiveDayChancePercent ?? 25;
- itemProperties.Light = new UpdLight { IsActive = randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0 };
+ itemUpd.Light = new UpdLight { IsActive = randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0 };
hasProperties = true;
}
else if (itemTemplate?.Parent == BaseClasses.TACTICAL_COMBO)
@@ -132,7 +135,7 @@ public class BotGeneratorHelper(
// Get chance from botconfig for bot type, use 50% if no value found
var lightLaserActiveChance = equipmentSettings?.LaserIsActiveChancePercent ?? 50;
- itemProperties.Light = new UpdLight { IsActive = randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0 };
+ itemUpd.Light = new UpdLight { IsActive = randomUtil.GetChance100(lightLaserActiveChance), SelectedMode = 0 };
hasProperties = true;
}
@@ -144,7 +147,7 @@ public class BotGeneratorHelper(
? equipmentSettings?.NvgIsActiveChanceNightPercent ?? 90
: equipmentSettings?.NvgIsActiveChanceDayPercent ?? 15;
- itemProperties.Togglable = new UpdTogglable { On = randomUtil.GetChance100(nvgActiveChance) };
+ itemUpd.Togglable = new UpdTogglable { On = randomUtil.GetChance100(nvgActiveChance) };
hasProperties = true;
}
@@ -152,12 +155,18 @@ public class BotGeneratorHelper(
if ((itemTemplate?.Properties?.HasHinge ?? false) && (itemTemplate.Properties.FaceShieldComponent ?? false))
{
var faceShieldActiveChance = equipmentSettings?.FaceShieldIsActiveChancePercent ?? 75;
- itemProperties.Togglable = new UpdTogglable { On = randomUtil.GetChance100(faceShieldActiveChance) };
+ itemUpd.Togglable = new UpdTogglable { On = randomUtil.GetChance100(faceShieldActiveChance) };
hasProperties = true;
}
- // Get chance from botconfig for bot type, use 75% if no value found
- return hasProperties ? itemProperties : null;
+ if (forceStackObjectsCount)
+ {
+ // Ensure property is set
+ itemUpd.StackObjectsCount ??= 1;
+ }
+
+ // Some items (weapon mods) may not have any props, and we don't want an empty Upd object
+ return hasProperties ? itemUpd : null;
}
///
@@ -168,17 +177,22 @@ public class BotGeneratorHelper(
/// Randomized value from maxHpResource
protected double GetRandomizedResourceValue(double maxResource, RandomisedResourceValues? randomizationValues)
{
- if (randomizationValues is null)
+ if (randomizationValues is null || randomUtil.GetChance100(randomizationValues.ChanceMaxResourcePercent))
{
return maxResource;
}
- if (randomUtil.GetChance100(randomizationValues.ChanceMaxResourcePercent))
+ if (maxResource.Approx(1))
{
- return maxResource;
+ return 1;
}
- return randomUtil.GetDouble(randomUtil.GetPercentOfValue(randomizationValues.ResourcePercent, maxResource, 0), maxResource);
+ var min = randomUtil.GetPercentOfValue(randomizationValues.ResourcePercent, maxResource, 0);
+
+ // Using food at 0 causes client to error
+ var clampedMin = Math.Clamp(min, 1, min);
+
+ return randomUtil.GetDouble(clampedMin, maxResource);
}
///