diff --git a/Libraries/Core/Generators/BotEquipmentModGenerator.cs b/Libraries/Core/Generators/BotEquipmentModGenerator.cs
index eaf37413..b667962b 100644
--- a/Libraries/Core/Generators/BotEquipmentModGenerator.cs
+++ b/Libraries/Core/Generators/BotEquipmentModGenerator.cs
@@ -860,7 +860,7 @@ public class BotEquipmentModGenerator(
request.RandomisationSettings.MinimumMagazineSize is not null
)
{
- modPool = GetFilterdMagazinePoolByCapacity(request, modPool);
+ modPool = GetFilterdMagazinePoolByCapacity(request, modPool).ToList();
}
// Pick random mod that's compatible
@@ -919,9 +919,24 @@ public class BotEquipmentModGenerator(
/// Pool of magazine tpls to filter
/// Filtered pool of magazine tpls
///
- public List GetFilterdMagazinePoolByCapacity(ModToSpawnRequest modSpawnRequest, List modPool)
+ public IEnumerable GetFilterdMagazinePoolByCapacity(ModToSpawnRequest modSpawnRequest, List modPool)
{
- throw new NotImplementedException();
+ var weaponTpl = modSpawnRequest.Weapon[0].Template;
+ modSpawnRequest.RandomisationSettings.MinimumMagazineSize.TryGetValue(weaponTpl, out var minMagSizeFromSettings);
+ var minMagazineSize = minMagSizeFromSettings;
+ var desiredMagazineTpls = modPool.Where((magTpl) => {
+ var magazineDb = _itemHelper.GetItem(magTpl).Value;
+ return magazineDb.Properties is not null && magazineDb.Properties.Cartridges.FirstOrDefault().MaxCount >= minMagazineSize;
+ });
+
+ if (!desiredMagazineTpls.Any())
+ {
+ _logger.Warning("Magazine size filter for ${ weaponTpl} was too strict, ignoring filter");
+
+ return modPool;
+ }
+
+ return desiredMagazineTpls;
}
///
@@ -1420,7 +1435,8 @@ public class BotEquipmentModGenerator(
var result = new List();
// Get item blacklist and mod equipment blacklist as one array
- var blacklist = _itemFilterService.GetBlacklistedItems().Concat(botEquipBlacklist.Equipment[modSlot]);
+ botEquipBlacklist.Equipment.TryGetValue(modSlot, out var equipmentBlacklistValues);
+ var blacklist = _itemFilterService.GetBlacklistedItems().Concat(equipmentBlacklistValues ?? []);
result = allowedMods.Where((tpl) => !blacklist.Contains(tpl)).ToList();
return result;
diff --git a/Libraries/Core/Generators/BotGenerator.cs b/Libraries/Core/Generators/BotGenerator.cs
index 3c4d65e9..3514c13e 100644
--- a/Libraries/Core/Generators/BotGenerator.cs
+++ b/Libraries/Core/Generators/BotGenerator.cs
@@ -668,8 +668,8 @@ public class BotGenerator(
// Common skills have additional props
if (isCommonSkills)
{
- ((Common)skillToAdd).PointsEarnedDuringSession = 0;
- ((Common)skillToAdd).LastAccess = 0;
+ skillToAdd.PointsEarnedDuringSession = 0;
+ skillToAdd.LastAccess = 0;
}
return skillToAdd;
diff --git a/Libraries/Core/Models/Eft/Common/Tables/BotType.cs b/Libraries/Core/Models/Eft/Common/Tables/BotType.cs
index c95094d9..3df1638a 100644
--- a/Libraries/Core/Models/Eft/Common/Tables/BotType.cs
+++ b/Libraries/Core/Models/Eft/Common/Tables/BotType.cs
@@ -2,6 +2,7 @@ using System.Text.Json.Serialization;
using Core.Models.Common;
using Core.Models.Enums;
using Core.Utils.Json.Converters;
+using SptCommon.Extensions;
namespace Core.Models.Eft.Common.Tables;
@@ -57,6 +58,19 @@ public record Appearance
[JsonPropertyName("voice")]
[JsonConverter(typeof(ArrayToObjectFactoryConverter))]
public Dictionary? Voice { get; set; }
+
+ public Dictionary this[string propName]
+ {
+ get
+ {
+ var matchingProp = GetType()
+ .GetProperties()
+ .SingleOrDefault(p => p.GetJsonName() == propName)
+ ?.GetValue(this);
+
+ return (Dictionary)matchingProp;
+ }
+ }
}
public record Chances
diff --git a/Libraries/Core/Services/BotEquipmentFilterService.cs b/Libraries/Core/Services/BotEquipmentFilterService.cs
index c8b490bb..c3ae5ce9 100644
--- a/Libraries/Core/Services/BotEquipmentFilterService.cs
+++ b/Libraries/Core/Services/BotEquipmentFilterService.cs
@@ -348,8 +348,45 @@ public class BotEquipmentFilterService
bool showEditWarnings = true)
{
//TODO, bad typing by key with method below due to, EquipmentSlots
- // MAKE CONSISTENT BEFORE IMPLEMENTING
- throw new NotImplementedException();
+ if (weightingAdjustments is null)
+ {
+ return;
+ }
+
+ if (weightingAdjustments.Add?.Count > 0)
+ {
+ foreach (var poolAdjustmentKvP in weightingAdjustments.Add)
+ {
+ var locationToUpdate = botItemPool[Enum.Parse(poolAdjustmentKvP.Key)];
+ foreach (var itemToAddKvP in poolAdjustmentKvP.Value)
+ {
+ locationToUpdate[itemToAddKvP.Key] = itemToAddKvP.Value;
+ }
+ }
+ }
+
+ if (weightingAdjustments.Edit?.Count > 0)
+ {
+ foreach (var poolAdjustmentKvP in weightingAdjustments.Edit)
+ {
+ var locationToUpdate = botItemPool[Enum.Parse(poolAdjustmentKvP.Key)];
+ foreach (var itemToEditKvP in poolAdjustmentKvP.Value)
+ {
+ // Only make change if item exists as we're editing, not adding
+ if (locationToUpdate[itemToEditKvP.Key] != null || locationToUpdate[itemToEditKvP.Key] == 0)
+ {
+ locationToUpdate[itemToEditKvP.Key] = itemToEditKvP.Value;
+ }
+ else
+ {
+ if (showEditWarnings)
+ {
+ _logger.Debug($"Tried to edit a non - existent item for slot: {poolAdjustmentKvP} {itemToEditKvP}");
+ }
+ }
+ }
+ }
+ }
}
///
@@ -414,6 +451,44 @@ public class BotEquipmentFilterService
Appearance botItemPool,
bool showEditWarnings = true)
{
- throw new NotImplementedException();
+ if (weightingAdjustments is null)
+ {
+ return;
+ }
+
+ if (weightingAdjustments.Add?.Count > 0)
+ {
+ foreach (var poolAdjustmentKvP in weightingAdjustments.Add)
+ {
+ var locationToUpdate = botItemPool[poolAdjustmentKvP.Key];
+ foreach (var itemToAddKvP in poolAdjustmentKvP.Value)
+ {
+ locationToUpdate[itemToAddKvP.Key] = itemToAddKvP.Value;
+ }
+ }
+ }
+
+ if (weightingAdjustments.Edit?.Count > 0)
+ {
+ foreach (var poolAdjustmentKvP in weightingAdjustments.Edit)
+ {
+ var locationToUpdate = botItemPool[poolAdjustmentKvP.Key];
+ foreach (var itemToEditKvP in poolAdjustmentKvP.Value)
+ {
+ // Only make change if item exists as we're editing, not adding
+ if (locationToUpdate[itemToEditKvP.Key] != null || locationToUpdate[itemToEditKvP.Key] == 0)
+ {
+ locationToUpdate[itemToEditKvP.Key] = itemToEditKvP.Value;
+ }
+ else
+ {
+ if (showEditWarnings)
+ {
+ _logger.Debug($"Tried to edit a non - existent item for slot: {poolAdjustmentKvP} {itemToEditKvP}");
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/Libraries/Core/Services/BotEquipmentModPoolService.cs b/Libraries/Core/Services/BotEquipmentModPoolService.cs
index 1b89b291..3f9c958a 100644
--- a/Libraries/Core/Services/BotEquipmentModPoolService.cs
+++ b/Libraries/Core/Services/BotEquipmentModPoolService.cs
@@ -1,18 +1,144 @@
-using SptCommon.Annotations;
+using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
+using Core.Models.Utils;
+using Core.Helpers;
+using Core.Servers;
+using Core.Models.Enums;
+using Core.Models.Spt.Config;
+using System.Collections.Generic;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class BotEquipmentModPoolService
{
+ protected ISptLogger _logger;
+ protected ItemHelper _itemHelper;
+ protected DatabaseService _databaseService;
+ protected LocalisationService _localisationService;
+ protected ConfigServer _configServer;
+
+ protected bool _weaponPoolGenerated = false;
+ protected Dictionary>> _weaponModPool;
+ protected Dictionary>> _gearModPool;
+ protected BotConfig _botConfig;
+
+ public BotEquipmentModPoolService(
+ ISptLogger logger,
+ ItemHelper itemHelper,
+ DatabaseService databaseService,
+ LocalisationService localisationService,
+ ConfigServer configServer
+ )
+ {
+ _logger = logger;
+ _itemHelper = itemHelper;
+ _databaseService = databaseService;
+ _localisationService = localisationService;
+ _configServer = configServer;
+ _botConfig = _configServer.GetConfig();
+
+ _weaponModPool = new Dictionary>>();
+ _gearModPool = new Dictionary>>();
+ }
+
/**
* Store dictionary of mods for each item passed in
* @param items items to find related mods and store in modPool
*/
- protected void GeneratePool(List items, string poolType)
+ protected void GeneratePool(IEnumerable? items, string poolType)
{
- throw new NotImplementedException();
+ if (items is null)
+ {
+ _logger.Error(_localisationService.GetText("bot-unable_to_generate_item_pool_no_items", poolType));
+
+ return;
+ }
+
+ // Get weapon or gear pool
+ var pool = poolType == "weapon" ? _weaponModPool : _gearModPool;
+ foreach (var item in items) {
+ if (item.Properties is null)
+ {
+ _logger.Error(_localisationService.GetText("bot-item_missing_props_property", new {
+ itemTpl = item.Id,
+ name = item.Name,
+ }));
+
+ continue;
+ }
+
+ // Skip item without slots
+ if (item.Properties.Slots is null || item.Properties.Slots.Count == 0)
+ {
+ continue;
+ }
+
+ // Add base item (weapon/armor) to pool
+ if (!pool.ContainsKey(item.Id))
+ {
+ pool[item.Id] = new Dictionary>();
+ }
+
+ // iterate over each items mod slots e.g. mod_muzzle
+ foreach (var slot in item.Properties.Slots) {
+ // Get mods that fit into the current mod slot
+ var itemsThatFit = slot.Props.Filters.FirstOrDefault().Filter;
+
+ // Get weapon/armor pool to add mod slots + mod tpls to
+
+ foreach (var itemToAddTpl in itemsThatFit)
+ {
+ if (!pool[item.Id].ContainsKey(slot.Name))
+ {
+ // Ensure Mod slot key + blank dict value exist
+ pool[item.Id][slot.Name] = new();
+ }
+
+ if (!pool[item.Id][slot.Name].Contains(itemToAddTpl))
+ {
+ // Add tpl to list keyed by mod slot
+ pool[item.Id][slot.Name].Add(itemToAddTpl);
+ }
+
+ 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);
+ }
+ }
+ }
+
+ //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);
+ // }
+ // }
+ // }
+ //}
+ }
}
/**
@@ -31,7 +157,13 @@ public class BotEquipmentModPoolService
*/
public List GetCompatibleModsForWeaponSlot(string itemTpl, string slotName)
{
- throw new NotImplementedException();
+ if (!_weaponPoolGenerated)
+ {
+ // Get every weapon in db and generate mod pool
+ GenerateWeaponPool();
+ }
+
+ return _weaponModPool[itemTpl][slotName].ToList();
}
/**
@@ -70,7 +202,12 @@ public class BotEquipmentModPoolService
*/
protected void GenerateWeaponPool()
{
- throw new NotImplementedException();
+ var weapons = _databaseService.GetItems().Values.Where(
+ (item) => item.Type == "Item" && _itemHelper.IsOfBaseclass(item.Id, BaseClasses.WEAPON));
+ GeneratePool(weapons, "weapon");
+
+ // Flag pool as being complete
+ _weaponPoolGenerated = true;
}
/**
diff --git a/Libraries/Core/Services/ItemFilterService.cs b/Libraries/Core/Services/ItemFilterService.cs
index 09506eff..1f300b76 100644
--- a/Libraries/Core/Services/ItemFilterService.cs
+++ b/Libraries/Core/Services/ItemFilterService.cs
@@ -71,7 +71,7 @@ public class ItemFilterService(
*/
public List GetBlacklistedItems()
{
- throw new NotImplementedException();
+ return _cloner.Clone(_itemConfig.Blacklist);
}
/**
diff --git a/Libraries/Core/Services/RepairService.cs b/Libraries/Core/Services/RepairService.cs
index 152dfc81..b6f8437a 100644
--- a/Libraries/Core/Services/RepairService.cs
+++ b/Libraries/Core/Services/RepairService.cs
@@ -1,16 +1,29 @@
-using System.Text.Json.Serialization;
+using System.Text.Json.Serialization;
+using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Repair;
using Core.Models.Enums;
+using Core.Utils;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class RepairService
{
+ private readonly RandomUtil _randomUtil;
+ private readonly WeightedRandomHelper _weightedRandomHelper;
+
+ public RepairService(
+ RandomUtil randomUtil,
+ WeightedRandomHelper weightedRandomHelper)
+ {
+ _randomUtil = randomUtil;
+ _weightedRandomHelper = weightedRandomHelper;
+ }
+
///
/// Use trader to repair an items durability
///