diff --git a/Libraries/Core/Generators/BotEquipmentModGenerator.cs b/Libraries/Core/Generators/BotEquipmentModGenerator.cs
index ba1bdf71..ca2531ef 100644
--- a/Libraries/Core/Generators/BotEquipmentModGenerator.cs
+++ b/Libraries/Core/Generators/BotEquipmentModGenerator.cs
@@ -147,7 +147,7 @@ public class BotEquipmentModGenerator(
// Choose random mod from pool and check its compatibility
string modTpl = null;
var found = false;
- var exhaustableModPool = CreateExhaustableArray(modPoolToChooseFrom);
+ var exhaustableModPool = CreateExhaustableArray(modPoolToChooseFrom.ToList());
while (exhaustableModPool.HasValues())
{
modTpl = exhaustableModPool.GetRandomValue();
@@ -220,7 +220,7 @@ public class BotEquipmentModGenerator(
/// The armor items db template
/// Array of plate tpls to choose from
public FilterPlateModsForSlotByLevelResult FilterPlateModsForSlotByLevel(GenerateEquipmentProperties settings, string modSlot,
- List existingPlateTplPool, TemplateItem armorItem)
+ HashSet existingPlateTplPool, TemplateItem armorItem)
{
throw new NotImplementedException();
}
@@ -359,8 +359,7 @@ public class BotEquipmentModGenerator(
continue;
}
- if (
- IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role)
+ if (!IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role)
)
{
continue;
@@ -837,7 +836,7 @@ public class BotEquipmentModGenerator(
);
if (onlyLowProfileGasBlocks.Count() > 0)
{
- modPool = onlyLowProfileGasBlocks.ToList();
+ modPool = onlyLowProfileGasBlocks.ToHashSet();
}
}
else if (request.WeaponStats.HasRearIronSight ?? false && modPool.Count() > 1)
@@ -848,7 +847,7 @@ public class BotEquipmentModGenerator(
);
if (onlyHighProfileGasBlocks.Count() > 0)
{
- modPool = onlyHighProfileGasBlocks.ToList();
+ modPool = onlyHighProfileGasBlocks.ToHashSet();
}
}
}
@@ -860,7 +859,7 @@ public class BotEquipmentModGenerator(
request.RandomisationSettings.MinimumMagazineSize is not null
)
{
- modPool = GetFilterdMagazinePoolByCapacity(request, modPool).ToList();
+ modPool = GetFilterdMagazinePoolByCapacity(request, modPool).ToHashSet();
}
// Pick random mod that's compatible
@@ -919,7 +918,7 @@ public class BotEquipmentModGenerator(
/// Pool of magazine tpls to filter
/// Filtered pool of magazine tpls
///
- public IEnumerable GetFilterdMagazinePoolByCapacity(ModToSpawnRequest modSpawnRequest, List modPool)
+ public IEnumerable GetFilterdMagazinePoolByCapacity(ModToSpawnRequest modSpawnRequest, HashSet modPool)
{
var weaponTpl = modSpawnRequest.Weapon[0].Template;
modSpawnRequest.RandomisationSettings.MinimumMagazineSize.TryGetValue(weaponTpl, out var minMagSizeFromSettings);
@@ -950,7 +949,7 @@ public class BotEquipmentModGenerator(
/// Array of weapon items chosen item will be added to
/// Name of slot picked mod will be placed into
/// Chosen weapon details
- public ChooseRandomCompatibleModResult GetCompatibleWeaponModTplForSlotFromPool(ModToSpawnRequest request, List modPool, Slot parentSlot,
+ public ChooseRandomCompatibleModResult GetCompatibleWeaponModTplForSlotFromPool(ModToSpawnRequest request, HashSet modPool, Slot parentSlot,
ModSpawn? choiceTypeEnum, List- weapon, string modSlotName)
{
// Filter out incompatible mods from pool
@@ -985,7 +984,7 @@ public class BotEquipmentModGenerator(
public ChooseRandomCompatibleModResult GetCompatibleModFromPool(List modPool, ModSpawn? modSpawnType, List
- weapon)
{
// Create exhaustable pool to pick mod item from
- var exhaustableModPool = CreateExhaustableArray(modPool);
+ var exhaustableModPool = CreateExhaustableArray(modPool.ToList());
// Create default response if no compatible item is found below
ChooseRandomCompatibleModResult chosenModResult = new()
@@ -1063,9 +1062,9 @@ public class BotEquipmentModGenerator(
return chosenModResult;
}
- public ExhaustableArray CreateExhaustableArray(List itemsToAddToArray) // TODO: this wont likely be needed, reimplement for C#
+ public ExhaustableArray CreateExhaustableArray(List itemsToAddToArray)
{
- return new ExhaustableArray(itemsToAddToArray, _randomUtil, _cloner);
+ return new ExhaustableArray(itemsToAddToArray.ToList(), _randomUtil, _cloner);
}
///
@@ -1074,7 +1073,7 @@ public class BotEquipmentModGenerator(
///
/// Tpls that are incompatible and should not be used
/// string array of compatible mod tpls with weapon
- public List GetFilteredModPool(List modPool, List tplBlacklist)
+ public List GetFilteredModPool(HashSet modPool, List tplBlacklist)
{
return modPool.Where((tpl) => !tplBlacklist.Contains(tpl)).ToList();
}
@@ -1088,7 +1087,7 @@ public class BotEquipmentModGenerator(
///
/// Mods root parent (weapon/equipment)
/// Array of mod tpls
- public List GetModPoolForSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
+ public HashSet GetModPoolForSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
{
// Mod is flagged as being default only, try and find it in globals
if (request.ModSpawnResult == ModSpawn.DEFAULT_MOD)
@@ -1105,7 +1104,7 @@ public class BotEquipmentModGenerator(
return request.ItemModPool[request.ModSlot];
}
- public List GetModPoolForDefaultSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
+ public HashSet GetModPoolForDefaultSlot(ModToSpawnRequest request, TemplateItem weaponTemplate)
{
var matchingModFromPreset = GetMatchingModFromPreset(request, weaponTemplate);
if (matchingModFromPreset is null)
@@ -1166,7 +1165,7 @@ public class BotEquipmentModGenerator(
var newListOfModsForSlot = parentSlotCompatibleItems.Where((tpl) => !request.ConflictingItemTpls.Contains(tpl));
if (newListOfModsForSlot.Count() > 0)
{
- return newListOfModsForSlot.ToList();
+ return newListOfModsForSlot.ToHashSet();
}
}
@@ -1353,7 +1352,7 @@ public class BotEquipmentModGenerator(
/// db object for modItem we get compatible mods from
/// Pool of mods we are adding to
/// A blacklist of items that cannot be picked
- public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate, Dictionary>> modPool,
+ public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate, Dictionary>> modPool,
EquipmentFilterDetails botEquipBlacklist)
{
var desiredSlotObject = modTemplate.Properties.Slots?.FirstOrDefault((slot) => slot.Name.Contains(desiredSlotName));
@@ -1369,7 +1368,7 @@ public class BotEquipmentModGenerator(
}
// Filter mods
- var filteredMods = FilterModsByBlacklist(supportedSubMods, botEquipBlacklist, desiredSlotName);
+ var filteredMods = FilterModsByBlacklist(supportedSubMods.ToHashSet(), botEquipBlacklist, desiredSlotName);
if (!filteredMods.Any())
{
_logger.Warning(
@@ -1390,7 +1389,7 @@ public class BotEquipmentModGenerator(
modPool[modTemplate.Id] = new();
}
- modPool[modTemplate.Id][desiredSlotObject.Name] = supportedSubMods;
+ modPool[modTemplate.Id][desiredSlotObject.Name] = supportedSubMods.ToHashSet();
}
///
@@ -1400,7 +1399,7 @@ public class BotEquipmentModGenerator(
/// Slot item should fit in
/// Equipment that should not be picked
/// Array of compatible items for that slot
- public List GetDynamicModPool(string parentItemId, string modSlot, EquipmentFilterDetails botEquipBlacklist)
+ public HashSet GetDynamicModPool(string parentItemId, string modSlot, EquipmentFilterDetails botEquipBlacklist)
{
var modsFromDynamicPool = _cloner.Clone(
_botEquipmentModPoolService.GetCompatibleModsForWeaponSlot(parentItemId, modSlot)
@@ -1424,7 +1423,7 @@ public class BotEquipmentModGenerator(
/// Equipment blacklist
/// Slot mods belong to
/// Filtered array of mod tpls
- public List FilterModsByBlacklist(List allowedMods, EquipmentFilterDetails? botEquipBlacklist, string modSlot)
+ public HashSet FilterModsByBlacklist(HashSet allowedMods, EquipmentFilterDetails? botEquipBlacklist, string modSlot)
{
// No blacklist, nothing to filter out
if (botEquipBlacklist is null)
@@ -1432,12 +1431,12 @@ public class BotEquipmentModGenerator(
return allowedMods;
}
- var result = new List();
+ var result = new HashSet();
// Get item blacklist and mod equipment blacklist as one array
botEquipBlacklist.Equipment.TryGetValue(modSlot, out var equipmentBlacklistValues);
var blacklist = _itemFilterService.GetBlacklistedItems().Concat(equipmentBlacklistValues ?? []);
- result = allowedMods.Where((tpl) => !blacklist.Contains(tpl)).ToList();
+ result = allowedMods.Where((tpl) => !blacklist.Contains(tpl)).ToHashSet();
return result;
}
@@ -1452,7 +1451,7 @@ public class BotEquipmentModGenerator(
/// ModPool which should include available cartridges
/// The CylinderMagazine's UID
/// The CylinderMagazine's template
- public void FillCamora(List
- items, Dictionary>> modPool, string cylinderMagParentId,
+ public void FillCamora(List
- items, Dictionary>> modPool, string cylinderMagParentId,
TemplateItem cylinderMagTemplate)
{
var itemModPool = modPool[cylinderMagTemplate.Id];
@@ -1474,7 +1473,7 @@ public class BotEquipmentModGenerator(
modPool[cylinderMagTemplate.Id] = new();
foreach (var camora in camoraSlots)
{
- modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters?[0].Filter;
+ modPool[cylinderMagTemplate.Id][camora.Name] = camora.Props.Filters?[0].Filter.ToHashSet();
}
itemModPool = modPool[cylinderMagTemplate.Id];
@@ -1485,12 +1484,12 @@ public class BotEquipmentModGenerator(
var camoraFirstSlot = "camora_000";
if (itemModPool.TryGetValue(modSlot, out var value))
{
- exhaustableModPool = CreateExhaustableArray(value);
+ exhaustableModPool = CreateExhaustableArray(value.ToList());
}
else if (itemModPool.ContainsKey(camoraFirstSlot))
{
modSlot = camoraFirstSlot;
- exhaustableModPool = CreateExhaustableArray(MergeCamoraPools(itemModPool));
+ exhaustableModPool = CreateExhaustableArray(MergeCamoraPools(itemModPool).ToList());
}
else
{
@@ -1531,12 +1530,12 @@ public class BotEquipmentModGenerator(
///
/// Dictionary of camoras we want to merge into one array
/// String array of shells for multiple camora sources
- public List MergeCamoraPools(Dictionary> camorasWithShells)
+ public HashSet MergeCamoraPools(Dictionary> camorasWithShells)
{
return camorasWithShells
.SelectMany(shellKvP => shellKvP.Value)
.Distinct()
- .ToList();
+ .ToHashSet();
}
///
@@ -1548,7 +1547,7 @@ public class BotEquipmentModGenerator(
/// Full scope pool
/// Whitelist of scope types by weapon base type
/// Array of scope tpls that have been filtered to just ones allowed for that weapon type
- public List FilterSightsByWeaponType(Item weapon, List scopes, Dictionary> botWeaponSightWhitelist)
+ public HashSet FilterSightsByWeaponType(Item weapon, HashSet scopes, Dictionary> botWeaponSightWhitelist)
{
var weaponDetails = _itemHelper.GetItem(weapon.Template);
@@ -1562,7 +1561,7 @@ public class BotEquipmentModGenerator(
}
// Filter items that are not directly scopes OR mounts that do not hold the type of scope we allow for this weapon type
- List filteredScopesAndMods = [];
+ HashSet filteredScopesAndMods = [];
foreach (var item in scopes)
{
// Mods is a scope, check base class is allowed
diff --git a/Libraries/Core/Generators/BotInventoryGenerator.cs b/Libraries/Core/Generators/BotInventoryGenerator.cs
index 58ac4554..3b2a4914 100644
--- a/Libraries/Core/Generators/BotInventoryGenerator.cs
+++ b/Libraries/Core/Generators/BotInventoryGenerator.cs
@@ -201,7 +201,7 @@ public class BotInventoryGenerator(
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
- GeneratingPlayerLevel = pmcProfile.Info.Level,
+ GeneratingPlayerLevel = pmcProfile.Info.Level
}
);
}
@@ -222,7 +222,7 @@ public class BotInventoryGenerator(
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
- GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE],
+ GenerateModsBlacklist = [ItemTpl.POCKETS_1X4_TUE, ItemTpl.POCKETS_LARGE],
GeneratingPlayerLevel = pmcProfile.Info.Level,
}
);
@@ -483,7 +483,7 @@ public class BotInventoryGenerator(
// Does item have slots for sub-mods to be inserted into
if (pickedItemDb.Properties?.Slots?.Count > 0
&& settings?.GenerateModsBlacklist is not null
- && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id))
+ && !settings.GenerateModsBlacklist.Contains(pickedItemDb.Id))
{
var childItemsToAdd = _botEquipmentModGenerator.GenerateModsForEquipment(
[item],
@@ -512,17 +512,20 @@ public class BotInventoryGenerator(
/// Item mod pool is being retrieved and filtered
/// Blacklist to filter mod pool with
/// Filtered pool of mods
- public Dictionary> GetFilteredDynamicModsForItem(string itemTpl, Dictionary> equipmentBlacklist)
+ public Dictionary> GetFilteredDynamicModsForItem(string itemTpl, Dictionary> equipmentBlacklist)
{
var modPool = _botEquipmentModPoolService.GetModsForGearSlot(itemTpl);
- foreach (var modSlot in modPool.Keys ?? Enumerable.Empty())
+ foreach (var modSlot in modPool)
{
- var blacklistedMods = equipmentBlacklist[modSlot] ?? [];
- var filteredMods = modPool[modSlot].Where((slotName) => !blacklistedMods.Contains(slotName));
+ // Get blacklist
+ equipmentBlacklist.TryGetValue(modSlot.Key, out var blacklistedMods);
+
+ // Get mods not on blacklist
+ var filteredMods = modPool[modSlot.Key].Where((slotName) => !(blacklistedMods ?? []).Contains(slotName));
if (filteredMods.Any())
{
- modPool[modSlot] = filteredMods.ToList();
+ modPool[modSlot.Key] = filteredMods.ToHashSet();
}
}
diff --git a/Libraries/Core/Generators/BotLootGenerator.cs b/Libraries/Core/Generators/BotLootGenerator.cs
index 56c3cb8b..0c6f37ef 100644
--- a/Libraries/Core/Generators/BotLootGenerator.cs
+++ b/Libraries/Core/Generators/BotLootGenerator.cs
@@ -446,7 +446,7 @@ public class BotLootGenerator(
return;
}
- var weightedItemTpl = _weightedRandomHelper.GetWeightedValue(pool);
+ var weightedItemTpl = _weightedRandomHelper.GetWeightedValue(pool);
var (key, itemToAddTemplate) = _itemHelper.GetItem(weightedItemTpl);
if (!key)
@@ -456,16 +456,13 @@ public class BotLootGenerator(
continue;
}
- if (itemSpawnLimits is not null)
+ if (itemSpawnLimits is not null && ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits))
{
- if (ItemHasReachedSpawnLimit(itemToAddTemplate, botRole, itemSpawnLimits))
- {
- // Remove item from pool to prevent it being picked again
- pool.Remove(weightedItemTpl);
+ // Remove item from pool to prevent it being picked again
+ pool.Remove(weightedItemTpl);
- i--;
- continue;
- }
+ i--;
+ continue;
}
var newRootItemId = _hashUtil.Generate();
@@ -541,9 +538,9 @@ public class BotLootGenerator(
if (fitItemIntoContainerAttempts >= 4)
{
_logger.Debug(
- $"Failed placing item: {i} of: {totalItemCount} items into: {botRole} " +
+ $"Failed placing item: {itemToAddTemplate.Name}: {i} of: {totalItemCount} items into: {botRole} " +
$"containers: {string.Join(",", equipmentSlots)}. Tried: {fitItemIntoContainerAttempts} " +
- $"times, reason: {itemAddedResult.ToString()}, skipping"
+ $"times, reason: {itemAddedResult}, skipping"
);
break;
diff --git a/Libraries/Core/Generators/BotWeaponGenerator.cs b/Libraries/Core/Generators/BotWeaponGenerator.cs
index 98b14008..6799090c 100644
--- a/Libraries/Core/Generators/BotWeaponGenerator.cs
+++ b/Libraries/Core/Generators/BotWeaponGenerator.cs
@@ -348,7 +348,7 @@ public class BotWeaponGenerator(
}
// Iterate over required slots in db item, check mod exists for that slot
- foreach (var modSlotTemplate in modTemplate.Properties.Slots.Where((slot) => slot.Required ?? false))
+ foreach (var modSlotTemplate in modTemplate.Properties.Slots?.Where((slot) => slot.Required.GetValueOrDefault(false)) ?? [])
{
var slotName = modSlotTemplate.Name;
var hasWeaponSlotItem = weaponItemList.Any(
@@ -553,9 +553,7 @@ public class BotWeaponGenerator(
protected string GetWeightedCompatibleAmmo(Dictionary> cartridgePool, TemplateItem weaponTemplate)
{
var desiredCaliber = GetWeaponCaliber(weaponTemplate);
-
- var cartridgePoolForWeapon = cartridgePool[desiredCaliber];
- if (cartridgePoolForWeapon is null || cartridgePoolForWeapon?.Count == 0)
+ if (!cartridgePool.TryGetValue(desiredCaliber, out var cartridgePoolForWeapon) || cartridgePoolForWeapon?.Keys.Count == 0)
{
_logger.Debug(
_localisationService.GetText(
diff --git a/Libraries/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs b/Libraries/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
index 409fe945..d5fcf315 100644
--- a/Libraries/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
+++ b/Libraries/Core/Generators/WeaponGen/Implementations/ExternalInventoryMagGen.cs
@@ -1,4 +1,4 @@
-using SptCommon.Annotations;
+using SptCommon.Annotations;
using Core.Helpers;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
@@ -31,7 +31,7 @@ public class ExternalInventoryMagGen(
public void Process(InventoryMagGen inventoryMagGen)
{
- // Cout of attempts to fit a magazine into bot inventory
+ // Count of attempts to fit a magazine into bot inventory
var fitAttempts = 0;
// Magazine Db template
@@ -41,6 +41,7 @@ public class ExternalInventoryMagGen(
List attemptedMagBlacklist = [];
var defaultMagazineTpl = _botWeaponGeneratorHelper.GetWeaponsDefaultMagazineTpl(weapon);
var randomizedMagazineCount = _botWeaponGeneratorHelper.GetRandomizedMagazineCount(inventoryMagGen.GetMagCount());
+ var isShotgun = _itemHelper.IsOfBaseclass(weapon.Id, BaseClasses.SHOTGUN);
for (var i = 0; i < randomizedMagazineCount; i++)
{
var magazineWithAmmo = _botWeaponGeneratorHelper.CreateMagazineWithAmmo(
@@ -99,16 +100,23 @@ public class ExternalInventoryMagGen(
break;
}
- // Edge case - some weapons (SKS) have an internal magazine as default, choose random non-internal magazine to add to bot instead
+ // Edge case - some weapons (SKS + shotguns) have an internal magazine as default, choose random non-internal magazine to add to bot instead
if (magTemplate.Properties.ReloadMagType == "InternalMagazine")
{
var result = GetRandomExternalMagazineForInternalMagazineGun(
inventoryMagGen.GetWeaponTemplate().Id,
attemptedMagBlacklist
);
+
if (result?.Id is null)
{
- _logger.Debug($"Unable to add additional magazine into bot inventory for weapon: {weapon.Name}, attempted: {fitAttempts} times");
+ // Highly likely shotgun has no external mags
+ if (isShotgun)
+ {
+ break;
+ }
+
+ _logger.Debug($"Unable to add additional magazine into bot inventory: vest/pockets for weapon: {weapon.Name}, attempted: {fitAttempts} times. Reason: {fitsIntoInventory}");
break;
}
diff --git a/Libraries/Core/Helpers/BotGeneratorHelper.cs b/Libraries/Core/Helpers/BotGeneratorHelper.cs
index 6b6c6550..4631c44b 100644
--- a/Libraries/Core/Helpers/BotGeneratorHelper.cs
+++ b/Libraries/Core/Helpers/BotGeneratorHelper.cs
@@ -582,7 +582,7 @@ public class BotGeneratorHelper(
{
parentItem.ParentId = container.Id;
parentItem.SlotId = slotGrid.Name;
- parentItem.Location = new ItemLocation()
+ parentItem.Location = new ItemLocation
{
X = findSlotResult.X,
Y = findSlotResult.Y,
diff --git a/Libraries/Core/Helpers/ContainerHelper.cs b/Libraries/Core/Helpers/ContainerHelper.cs
index af2c50b0..03c46552 100644
--- a/Libraries/Core/Helpers/ContainerHelper.cs
+++ b/Libraries/Core/Helpers/ContainerHelper.cs
@@ -28,39 +28,38 @@ public class ContainerHelper
return new FindSlotResult(false);
}
- // Down
+ // Down = y
for (var y = 0; y < limitY; y++)
{
- // Across
+
if (container2D[y].All((x) => x == 1))
{
// Every item in row is full, skip row
continue;
}
+ // Try each slot on the row (across = x)
for (var x = 0; x < limitX; x++)
{
var foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemWidth, itemHeight);
+ if (foundSlot)
+ {
+ return new FindSlotResult(true, x, y, rotation);
+ }
// Failed to find slot, rotate item and try again
- if (!foundSlot && itemWidth * itemHeight > 1)
+ if (!foundSlot && ItemBiggerThan1X1(itemWidth, itemHeight))
{
- // Bigger than 1x1
+ // Bigger than 1x1, try rotating
foundSlot = LocateSlot(container2D, containerX, containerY, x, y, itemHeight, itemWidth); // Height/Width swapped
if (foundSlot)
{
// Found a slot for it when rotated
rotation = true;
+
+ return new FindSlotResult(true, x, y, rotation);
}
}
-
- if (!foundSlot)
- {
- // Didn't fit this hole, try again
- continue;
- }
-
- return new FindSlotResult(true, x, y, rotation);
}
}
@@ -68,6 +67,11 @@ public class ContainerHelper
return new FindSlotResult(false);
}
+ private bool ItemBiggerThan1X1(int itemWidth, int itemHeight)
+ {
+ return itemWidth * itemHeight > 1;
+ }
+
///
/// Find a slot inside a container an item can be placed in
///
diff --git a/Libraries/Core/Helpers/DurabilityLimitsHelper.cs b/Libraries/Core/Helpers/DurabilityLimitsHelper.cs
index 2871d8ad..74205031 100644
--- a/Libraries/Core/Helpers/DurabilityLimitsHelper.cs
+++ b/Libraries/Core/Helpers/DurabilityLimitsHelper.cs
@@ -163,6 +163,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Weapon.LowestMax;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Weapon.LowestMax;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var durability);
return durability.Weapon.LowestMax;
}
@@ -174,6 +179,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Weapon.HighestMax;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Weapon.HighestMax;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var durability);
return durability.Weapon.HighestMax;
}
@@ -211,6 +221,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Weapon.MinDelta;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Weapon.MinDelta;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Weapon.MinDelta;
@@ -223,6 +238,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Weapon.HighestMax;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Weapon.HighestMax;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Weapon.HighestMax;
@@ -235,6 +255,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Armor.MinDelta;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Armor.MinDelta;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Armor.MinDelta;
@@ -247,6 +272,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Armor.MaxDelta;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Armor.MaxDelta;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Armor.MaxDelta;
@@ -259,6 +289,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Armor.MinLimitPercent;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Armor.MinLimitPercent;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Armor.MinLimitPercent;
@@ -271,6 +306,11 @@ public class DurabilityLimitsHelper(
return _botConfig.Durability.Default.Weapon.MinLimitPercent;
}
+ if (botRole == "pmc")
+ {
+ return _botConfig.Durability.Pmc.Weapon.MinLimitPercent;
+ }
+
_botConfig.Durability.BotDurabilities.TryGetValue(botRole, out var value);
return value.Weapon.MinLimitPercent;
diff --git a/Libraries/Core/Helpers/InventoryHelper.cs b/Libraries/Core/Helpers/InventoryHelper.cs
index dc90af7d..34f258ee 100644
--- a/Libraries/Core/Helpers/InventoryHelper.cs
+++ b/Libraries/Core/Helpers/InventoryHelper.cs
@@ -336,7 +336,17 @@ public class InventoryHelper(
/// Two-dimensional representation of container
protected int[][] GetBlankContainerMap(int containerH, int containerY)
{
- return Enumerable.Repeat(Enumerable.Repeat(0, containerH).ToArray(), containerY).ToArray();
+ //var x = new int[containerY][];
+ //for (int i = 0; i < containerY; i++)
+ //{
+ // x[i] = new int[containerH];
+ //}
+
+ //return x;
+
+ return Enumerable.Range(0, containerY)
+ .Select(i => new int[containerH])
+ .ToArray();
}
///
@@ -395,12 +405,12 @@ public class InventoryHelper(
}
// Fill the corresponding cells in the container map to show the slot is taken
- Array.Fill(containerRow, 1, itemLocation.X.Value, fillTo.Value);
+ Array.Fill(containerRow, 1, itemLocation.X.Value, fW);
} catch (Exception ex) {
_logger.Error(
_localisationService.GetText("inventory-unable_to_fill_container", new {
id = item.Id,
- error = ex.Message
+ error = $"{ex.Message} {ex.StackTrace}"
})
);
}
diff --git a/Libraries/Core/Helpers/ItemHelper.cs b/Libraries/Core/Helpers/ItemHelper.cs
index 8f51b412..f43b16df 100644
--- a/Libraries/Core/Helpers/ItemHelper.cs
+++ b/Libraries/Core/Helpers/ItemHelper.cs
@@ -1459,9 +1459,10 @@ public class ItemHelper(
}
// Get max number of cartridges in magazine, choose random value between min/max
+ var magProps = magTemplate.Properties;
var magazineCartridgeMaxCount = IsOfBaseclass(magTemplate.Id, BaseClasses.SPRING_DRIVEN_CYLINDER)
- ? magTemplate.Properties?.Slots?.Count() // Edge case for rotating grenade launcher magazine
- : magTemplate.Properties?.Cartridges[0]?.MaxCount;
+ ? magProps?.Slots?.Count // Edge case for rotating grenade launcher magazine
+ : magProps?.Cartridges.FirstOrDefault()?.MaxCount;
if (magazineCartridgeMaxCount is null)
{
diff --git a/Libraries/Core/Models/Eft/Common/Tables/GlobalTablesUsings.cs b/Libraries/Core/Models/Eft/Common/Tables/GlobalTablesUsings.cs
index 6c39a9bf..aa4ce584 100644
--- a/Libraries/Core/Models/Eft/Common/Tables/GlobalTablesUsings.cs
+++ b/Libraries/Core/Models/Eft/Common/Tables/GlobalTablesUsings.cs
@@ -1,2 +1,2 @@
-global using GlobalAmmo = System.Collections.Generic.Dictionary>;
-global using GlobalMods = System.Collections.Generic.Dictionary>>;
+global using GlobalAmmo = System.Collections.Generic.Dictionary>;
+global using GlobalMods = System.Collections.Generic.Dictionary>>;
diff --git a/Libraries/Core/Models/Spt/Bots/FilterPlateModsForSlotByLevelResult.cs b/Libraries/Core/Models/Spt/Bots/FilterPlateModsForSlotByLevelResult.cs
index 7f8cebd4..899f6872 100644
--- a/Libraries/Core/Models/Spt/Bots/FilterPlateModsForSlotByLevelResult.cs
+++ b/Libraries/Core/Models/Spt/Bots/FilterPlateModsForSlotByLevelResult.cs
@@ -8,7 +8,7 @@ public record FilterPlateModsForSlotByLevelResult
public Result? Result { get; set; }
[JsonPropertyName("plateModTpls")]
- public List? PlateModTemplates { get; set; }
+ public HashSet? PlateModTemplates { get; set; }
}
public enum Result
diff --git a/Libraries/Core/Models/Spt/Bots/ModToSpawnRequest.cs b/Libraries/Core/Models/Spt/Bots/ModToSpawnRequest.cs
index 4169edaf..a5a8edad 100644
--- a/Libraries/Core/Models/Spt/Bots/ModToSpawnRequest.cs
+++ b/Libraries/Core/Models/Spt/Bots/ModToSpawnRequest.cs
@@ -38,7 +38,7 @@ public record ModToSpawnRequest
/// Pool of items to pick from
///
[JsonPropertyName("itemModPool")]
- public Dictionary>? ItemModPool { get; set; }
+ public Dictionary>? ItemModPool { get; set; }
///
/// List with only weapon tpl in it, ready for mods to be added
diff --git a/Libraries/Core/Services/BotEquipmentModPoolService.cs b/Libraries/Core/Services/BotEquipmentModPoolService.cs
index 3f9c958a..f2ba4968 100644
--- a/Libraries/Core/Services/BotEquipmentModPoolService.cs
+++ b/Libraries/Core/Services/BotEquipmentModPoolService.cs
@@ -5,7 +5,6 @@ using Core.Helpers;
using Core.Servers;
using Core.Models.Enums;
using Core.Models.Spt.Config;
-using System.Collections.Generic;
namespace Core.Services;
@@ -18,7 +17,8 @@ public class BotEquipmentModPoolService
protected LocalisationService _localisationService;
protected ConfigServer _configServer;
- protected bool _weaponPoolGenerated = false;
+ protected bool _weaponPoolGenerated;
+ protected bool _armorPoolGenerated;
protected Dictionary>> _weaponModPool;
protected Dictionary>> _gearModPool;
protected BotConfig _botConfig;
@@ -110,34 +110,6 @@ public class BotEquipmentModPoolService
}
}
}
-
- //foreach (var slot in item.Properties.Slots)
- //{
- // var itemsThatFit = slot.Props.Filters[0].Filter;
- // foreach (var itemToAddTpl in itemsThatFit)
- // {
- // // Ensure key/value exists
- // pool.TryAdd(slot.Name, new Dictionary>());
- // pool.TryGetValue(item.Id, out var poolToAddTo);
- // poolToAddTo ??= new Dictionary>();
-
- // // only add item to pool if it doesn't already exist
- // poolToAddTo.TryAdd(slot.Name, []);
- // if (!poolToAddTo[slot.Name].Any(x => x == itemToAddTpl))
- // {
- // poolToAddTo[slot.Name].Add(itemToAddTpl);
-
- // // Check item added into array for slots, need to iterate over those
- // var subItemDetails = _itemHelper.GetItem(itemToAddTpl).Value;
- // var hasSubItemsToAdd = (subItemDetails?.Properties?.Slots?.Count ?? 0) > 0;
- // if (hasSubItemsToAdd && !pool.ContainsKey(subItemDetails.Id))
- // {
- // // Recursive call
- // GeneratePool([subItemDetails], poolType);
- // }
- // }
- // }
- //}
}
}
@@ -150,12 +122,12 @@ public class BotEquipmentModPoolService
}
/**
- * Get array of compatible mods for an items mod slot (generate pool if it doesnt exist already)
+ * Get array of compatible mods for an items mod slot (generate pool if it doesn't exist already)
* @param itemTpl item to look up
* @param slotName slot to get compatible mods for
* @returns tpls that fit the slot
*/
- public List GetCompatibleModsForWeaponSlot(string itemTpl, string slotName)
+ public HashSet GetCompatibleModsForWeaponSlot(string itemTpl, string slotName)
{
if (!_weaponPoolGenerated)
{
@@ -163,18 +135,7 @@ public class BotEquipmentModPoolService
GenerateWeaponPool();
}
- return _weaponModPool[itemTpl][slotName].ToList();
- }
-
- /**
- * Get array of compatible mods for an items mod slot (generate pool if it doesnt exist already)
- * @param itemTpl item to look up
- * @param slotName slot to get compatible mods for
- * @returns tpls that fit the slot
- */
- public List GetCompatibleModsForGearSlot(string itemTpl, string slotName)
- {
- throw new NotImplementedException();
+ return _weaponModPool[itemTpl][slotName];
}
/**
@@ -182,9 +143,16 @@ public class BotEquipmentModPoolService
* @param itemTpl items tpl to look up mods for
* @returns Dictionary of mods (keys are mod slot names) with array of compatible mod tpls as value
*/
- public Dictionary> GetModsForGearSlot(string itemTpl)
+ public Dictionary>? GetModsForGearSlot(string itemTpl)
{
- throw new NotImplementedException();
+ if (!_armorPoolGenerated)
+ {
+ GenerateGearPool();
+ }
+
+ return _gearModPool.TryGetValue(itemTpl, out var value)
+ ? value
+ : [];
}
/**
@@ -192,9 +160,14 @@ public class BotEquipmentModPoolService
* @param itemTpl Weapons tpl to look up mods for
* @returns Dictionary of mods (keys are mod slot names) with array of compatible mod tpls as value
*/
- public Dictionary> GetModsForWeaponSlot(string itemTpl)
+ public Dictionary> GetModsForWeaponSlot(string itemTpl)
{
- throw new NotImplementedException();
+ if (!_weaponPoolGenerated)
+ {
+ GenerateWeaponPool();
+ }
+
+ return _weaponModPool[itemTpl];
}
/**
@@ -215,6 +188,17 @@ public class BotEquipmentModPoolService
*/
protected void GenerateGearPool()
{
- throw new NotImplementedException();
+ var gear = _databaseService.GetItems().Values.Where(
+ (item) => item.Type == "Item"
+ && _itemHelper.IsOfBaseclasses(item.Id, [
+ BaseClasses.ARMORED_EQUIPMENT,
+ BaseClasses.VEST,
+ BaseClasses.ARMOR,
+ BaseClasses.HEADWEAR,
+ ]));
+ GeneratePool(gear, "gear");
+
+ // Flag pool as being complete
+ _armorPoolGenerated = true;
}
}
diff --git a/Libraries/Core/Services/RepairService.cs b/Libraries/Core/Services/RepairService.cs
index b6f8437a..d8b75228 100644
--- a/Libraries/Core/Services/RepairService.cs
+++ b/Libraries/Core/Services/RepairService.cs
@@ -6,6 +6,7 @@ using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Repair;
using Core.Models.Enums;
+using Core.Models.Utils;
using Core.Utils;
namespace Core.Services;
@@ -13,13 +14,16 @@ namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class RepairService
{
+ private readonly ISptLogger _logger;
private readonly RandomUtil _randomUtil;
private readonly WeightedRandomHelper _weightedRandomHelper;
public RepairService(
+ ISptLogger _logger,
RandomUtil randomUtil,
WeightedRandomHelper weightedRandomHelper)
{
+ this._logger = _logger;
_randomUtil = randomUtil;
_weightedRandomHelper = weightedRandomHelper;
}
@@ -165,10 +169,26 @@ public class RepairService
/// Add random buff to item
///
/// weapon/armor config
- /// Details for item to repair
- public void AddBuff(Core.Models.Spt.Config.BonusSettings itemConfig, Item item)
+ /// Item to repair
+ public void AddBuff(Models.Spt.Config.BonusSettings itemConfig, Item item)
{
- throw new NotImplementedException();
+ _logger.Error("NOT IMPLEMENTED - AddBuff");
+ //var bonusRarity = _weightedRandomHelper.GetWeightedValue(itemConfig.RarityWeight);
+ //var bonusType = _weightedRandomHelper.GetWeightedValue(itemConfig.BonusTypeWeight);
+
+ //var bonusValues = itemConfig[bonusRarity][bonusType].valuesMinMax;
+ //var bonusValue = _randomUtil.GetFloat(bonusValues.min, bonusValues.max);
+
+ //var bonusThresholdPercents = itemConfig[bonusRarity][bonusType].activeDurabilityPercentMinMax;
+ //var bonusThresholdPercent = _randomUtil.GetInt(bonusThresholdPercents.min, bonusThresholdPercents.max);
+
+ //item.Upd.Buff = new UpdBuff {
+ // Rarity = bonusRarity,
+ // BuffType = bonusType,
+ // Value = bonusValue,
+ // ThresholdDurability = _randomUtil.GetPercentOfValue(bonusThresholdPercent, item.Upd.Repairable.Durability, 2).toFixed(2),
+ // )
+ //};
}
///
diff --git a/Server/Assets/configs/bot.json b/Server/Assets/configs/bot.json
index d8203cd6..671ec148 100644
--- a/Server/Assets/configs/bot.json
+++ b/Server/Assets/configs/bot.json
@@ -88,7 +88,6 @@
"minLimitPercent": 15
}
},
- "botDurabilities": {
"pmc": {
"armor": {
"lowestMaxPercent": 90,
@@ -105,6 +104,7 @@
"minLimitPercent": 15
}
},
+ "botDurabilities": {
"boss": {
"armor": {
"lowestMaxPercent": 90,
diff --git a/Server/Assets/database/bots/types/bear.json b/Server/Assets/database/bots/types/bear.json
index 29d1f29d..4e00c3af 100644
--- a/Server/Assets/database/bots/types/bear.json
+++ b/Server/Assets/database/bots/types/bear.json
@@ -2826,6 +2826,10 @@
"5efb0d4f4bc50b58e81710f3": 9,
"5efb0fc6aeb21837e749c801": 9
},
+ "Caliber127x33": {
+ "668fe62ac62660a5d8071446": 2,
+ "66a0d1e0ed648d72fe064d06": 5
+ },
"Caliber127x55": {
"5cadf6ddae9215051e1c23b2": 8,
"5cadf6e5ae921500113bb973": 4,
diff --git a/Server/Assets/database/bots/types/usec.json b/Server/Assets/database/bots/types/usec.json
index 5d2d531b..a0802984 100644
--- a/Server/Assets/database/bots/types/usec.json
+++ b/Server/Assets/database/bots/types/usec.json
@@ -2818,6 +2818,10 @@
"5efb0d4f4bc50b58e81710f3": 9,
"5efb0fc6aeb21837e749c801": 9
},
+ "Caliber127x33": {
+ "668fe62ac62660a5d8071446": 2,
+ "66a0d1e0ed648d72fe064d06": 5
+ },
"Caliber127x55": {
"5cadf6ddae9215051e1c23b2": 8,
"5cadf6e5ae921500113bb973": 4,