diff --git a/Core/Generators/BotLootGenerator.cs b/Core/Generators/BotLootGenerator.cs
index 309e477c..7ff69f5a 100644
--- a/Core/Generators/BotLootGenerator.cs
+++ b/Core/Generators/BotLootGenerator.cs
@@ -524,24 +524,25 @@ public class BotLootGenerator
// Is Simple-Wallet / WZ wallet
if (_botConfig.WalletLoot.WalletTplPool.Contains(weightedItemTpl))
{
- var addCurrencyToWallet = _randomUtil.GetChance100(_botConfig.WalletLoot.ChancePercent);
+ var addCurrencyToWallet = _randomUtil.GetChance100(_botConfig.WalletLoot.ChancePercent);
if (addCurrencyToWallet)
{
// Create the currency items we want to add to wallet
- var itemsToAdd = CreateWalletLoot(newRootItemId);
+ var itemsToAdd = CreateWalletLoot(newRootItemId);
// Get the container grid for the wallet
- var containerGrid = _inventoryHelper.GetContainerSlotMap(weightedItemTpl);
+ var containerGrid = _inventoryHelper.GetContainerSlotMap(weightedItemTpl);
// Check if all the chosen currency items fit into wallet
- var canAddToContainer = _inventoryHelper.CanPlaceItemsInContainer(
+ var canAddToContainer = _inventoryHelper.CanPlaceItemsInContainer(
_cloner.Clone(containerGrid), // MUST clone grid before passing in as function modifies grid
itemsToAdd
);
if (canAddToContainer)
{
// Add each currency to wallet
- foreach ( var itemToAdd in itemsToAdd) {
+ foreach (var itemToAdd in itemsToAdd)
+ {
_inventoryHelper.PlaceItemInContainer(
containerGrid,
itemToAdd,
@@ -559,7 +560,7 @@ public class BotLootGenerator
AddRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc, botRole);
// Attempt to add item to container(s)
- var itemAddedResult = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
+ var itemAddedResult = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
equipmentSlots,
newRootItemId,
itemToAddTemplate.Id,
@@ -581,7 +582,11 @@ public class BotLootGenerator
fitItemIntoContainerAttempts++;
if (fitItemIntoContainerAttempts >= 4)
{
- _logger.Debug($"Failed placing item: { i } of: { totalItemCount } items into: { botRole } containers: { string.Join(",", equipmentSlots) }. Tried: { fitItemIntoContainerAttempts } times, reason: { itemAddedResult.ToString() }, skipping");
+ _logger.Debug(
+ $"Failed placing item: {i} of: {totalItemCount} items into: {botRole} " +
+ $"containers: {string.Join(",", equipmentSlots)}. Tried: {fitItemIntoContainerAttempts} " +
+ $"times, reason: {itemAddedResult.ToString()}, skipping"
+ );
break;
}
@@ -614,7 +619,34 @@ public class BotLootGenerator
///
public List> CreateWalletLoot(string walletId)
{
- throw new NotImplementedException();
+ List> result = [];
+
+ // Choose how many stacks of currency will be added to wallet
+ var itemCount = _randomUtil.GetInt(
+ (int)_botConfig.WalletLoot.ItemCount.Min,
+ (int)_botConfig.WalletLoot.ItemCount.Max
+ );
+ for (var index = 0; index < itemCount; index++)
+ {
+ // Choose the size of the currency stack - default is 5k, 10k, 15k, 20k, 25k
+ var chosenStackCount = _weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.StackSizeWeight);
+ List- items = new List
- ();
+ items.Add(
+ new Item
+ {
+ Id = _hashUtil.Generate(),
+ Template = _weightedRandomHelper.GetWeightedValue(_botConfig.WalletLoot.CurrencyWeight),
+ ParentId = walletId,
+ Upd = new()
+ {
+ StackObjectsCount = double.Parse(chosenStackCount)
+ }
+ }
+ );
+ result.Add(items);
+ }
+
+ return result;
}
///
@@ -626,7 +658,26 @@ public class BotLootGenerator
/// role bot has that owns item
public void AddRequiredChildItemsToParent(TemplateItem itemToAddTemplate, List
- itemToAddChildrenTo, bool isPmc, string botRole)
{
- throw new NotImplementedException();
+ // Fill ammo box
+ if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO_BOX))
+ {
+ _itemHelper.AddCartridgesToAmmoBox(itemToAddChildrenTo, itemToAddTemplate);
+ }
+ // Make money a stack
+ else if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.MONEY))
+ {
+ RandomiseMoneyStackSize(botRole, itemToAddTemplate, itemToAddChildrenTo[0]);
+ }
+ // Make ammo a stack
+ else if (_itemHelper.IsOfBaseclass(itemToAddTemplate.Id, BaseClasses.AMMO))
+ {
+ RandomiseAmmoStackSize(isPmc, itemToAddTemplate, itemToAddChildrenTo[0]);
+ }
+ // Must add soft inserts/plates
+ else if (_itemHelper.ItemRequiresSoftInserts(itemToAddTemplate.Id))
+ {
+ _itemHelper.AddChildSlotItems(itemToAddChildrenTo, itemToAddTemplate, null, false);
+ }
}
///
@@ -645,13 +696,53 @@ public class BotLootGenerator
BotBaseInventory botInventory,
EquipmentSlots equipmentSlot,
BotTypeInventory templateInventory,
- Dictionary modsChances,
+ Dictionary modChances,
string botRole,
bool isPmc,
int botLevel,
List? containersIdFull) // TODO: type for containersIdFull was Set
{
- throw new NotImplementedException();
+ var chosenWeaponType = _randomUtil.GetArrayValue(
+ [
+ EquipmentSlots.FirstPrimaryWeapon.ToString(),
+ EquipmentSlots.FirstPrimaryWeapon.ToString(),
+ EquipmentSlots.FirstPrimaryWeapon.ToString(),
+ EquipmentSlots.Holster.ToString()
+ ]
+ );
+ var randomisedWeaponCount = _randomUtil.GetInt(
+ (int)_pmcConfig.LooseWeaponInBackpackLootMinMax.Min,
+ (int)_pmcConfig.LooseWeaponInBackpackLootMinMax.Max
+ );
+ if (randomisedWeaponCount > 0)
+ {
+ for (var i = 0; i < randomisedWeaponCount; i++)
+ {
+ var generatedWeapon = _botWeaponGenerator.GenerateRandomWeapon(
+ sessionId,
+ chosenWeaponType,
+ templateInventory,
+ botInventory.Equipment,
+ modChances,
+ botRole,
+ isPmc,
+ botLevel
+ );
+ var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
+ [equipmentSlot],
+ generatedWeapon.Weapon[0].Id,
+ generatedWeapon.Weapon[0].Template,
+ generatedWeapon.Weapon,
+ botInventory,
+ containersIdFull
+ );
+
+ if (result != ItemAddedResult.SUCCESS)
+ {
+ _logger.Debug($"Failed to add additional weapon {generatedWeapon.Weapon[0].Id} to bot backpack, reason: {result.ToString()}");
+ }
+ }
+ }
}
///
@@ -662,7 +753,12 @@ public class BotLootGenerator
///
public void InitItemLimitArray(string botRole, Dictionary limitCount)
{
- throw new NotImplementedException();
+ // Init current count of items we want to limit
+ var spawnLimits = GetItemSpawnLimitsForBotType(botRole);
+ foreach (var limit in spawnLimits)
+ {
+ spawnLimits[limit.Key] = 0;
+ }
}
///
@@ -674,7 +770,45 @@ public class BotLootGenerator
/// true if item has reached spawn limit
public bool ItemHasReachedSpawnLimit(TemplateItem itemTemplate, string botRole, ItemSpawnLimitSettings itemSpawnLimits)
{
- throw new NotImplementedException();
+ // PMCs and scavs have different sections of bot config for spawn limits
+ if (itemSpawnLimits is not null && itemSpawnLimits.GlobalLimits?.Count == 0) {
+ // No items found in spawn limit, drop out
+ return false;
+ }
+
+ // No spawn limits, skipping
+ if (itemSpawnLimits is null) {
+ return false;
+ }
+
+ var idToCheckFor = GetMatchingIdFromSpawnLimits(itemTemplate, itemSpawnLimits.GlobalLimits);
+ if (idToCheckFor is null) {
+ // ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
+ return false;
+ }
+
+ // Increment item count with this bot type
+ itemSpawnLimits.CurrentLimits[idToCheckFor]++;
+
+ // Check if over limit
+ if (itemSpawnLimits.CurrentLimits[idToCheckFor] > itemSpawnLimits.GlobalLimits[idToCheckFor]) {
+ // Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
+ if (itemSpawnLimits.CurrentLimits[idToCheckFor] > itemSpawnLimits.CurrentLimits[idToCheckFor] * 10) {
+ _logger.Debug(
+ _localisationService.GetText("bot-item_spawn_limit_reached_skipping_item", new {
+ botRole = botRole,
+ itemName = itemTemplate.Name,
+ attempts = itemSpawnLimits.CurrentLimits[idToCheckFor]
+ })
+ );
+
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
}
///
@@ -685,7 +819,17 @@ public class BotLootGenerator
/// Money item to randomise
public void RandomiseMoneyStackSize(string botRole, TemplateItem itemTemplate, Item moneyItem)
{
- throw new NotImplementedException();
+ // Get all currency weights for this bot type
+ var currencyWeights = _botConfig.CurrencyStackSize[botRole];
+ if (currencyWeights is null) {
+ currencyWeights = new ();
+ }
+
+ var currencyWeight = currencyWeights[moneyItem.Template];
+
+ _itemHelper.AddUpdObjectToItem(moneyItem);
+
+ moneyItem.Upd.StackObjectsCount = double.Parse(_weightedRandomHelper.GetWeightedValue(currencyWeight));
}
///
@@ -696,7 +840,10 @@ public class BotLootGenerator
/// Ammo item to randomise
public void RandomiseAmmoStackSize(bool isPmc, TemplateItem itemTemplate, Item ammoItem)
{
- throw new NotImplementedException();
+ var randomSize = _itemHelper.GetRandomisedAmmoStackSize(itemTemplate);
+ _itemHelper.AddUpdObjectToItem(ammoItem);
+
+ ammoItem.Upd.StackObjectsCount = randomSize;
}
///
@@ -707,7 +854,17 @@ public class BotLootGenerator
/// Dictionary of tplIds and limit
public Dictionary GetItemSpawnLimitsForBotType(string botRole)
{
- throw new NotImplementedException();
+ if (_botHelper.IsBotPmc(botRole)) {
+ return _botConfig.ItemSpawnLimits["pmc"];
+ }
+
+ if (_botConfig.ItemSpawnLimits[botRole.ToLower()] is not null) {
+ return _botConfig.ItemSpawnLimits[botRole.ToLower()];
+ }
+
+ _logger.Warning(_localisationService.GetText("bot-unable_to_find_spawn_limits_fallback_to_defaults", botRole));
+
+ return new ();
}
///
@@ -716,8 +873,18 @@ public class BotLootGenerator
/// item we want to look for in spawn limits
/// Limits to check for item
/// id as string, otherwise undefined
- public string? GetMatchingIdFromSpawnLimits(TemplateItem itemTemplate, Dictionary spawnLimits)
+ public string? GetMatchingIdFromSpawnLimits(TemplateItem itemTemplate, Dictionary spawnLimits)
{
- throw new NotImplementedException();
+ if (spawnLimits.ContainsKey(itemTemplate.Id)) {
+ return itemTemplate.Id;
+ }
+
+ // tplId not found in spawnLimits, check if parentId is
+ if (spawnLimits.ContainsKey(itemTemplate.Parent)) {
+ return itemTemplate.Parent;
+ }
+
+ // parentId and tplid not found
+ return null;
}
}
diff --git a/Core/Models/Spt/Config/BotConfig.cs b/Core/Models/Spt/Config/BotConfig.cs
index 1fc63b7e..82b8b26f 100644
--- a/Core/Models/Spt/Config/BotConfig.cs
+++ b/Core/Models/Spt/Config/BotConfig.cs
@@ -38,7 +38,7 @@ public class BotConfig : BaseConfig
/** Control how many items are allowed to spawn on a bot
* key: bottype, value: */
[JsonPropertyName("itemSpawnLimits")]
- public Dictionary> ItemSpawnLimits { get; set; }
+ public Dictionary> ItemSpawnLimits { get; set; }
/** Blacklist/whitelist items on a bot */
[JsonPropertyName("equipment")]
@@ -78,7 +78,7 @@ public class BotConfig : BaseConfig
/** Currency weights, Keyed by botrole / currency */
[JsonPropertyName("currencyStackSize")]
- public Dictionary>> CurrencyStackSize { get; set; }
+ public Dictionary>> CurrencyStackSize { get; set; }
/** Tpls for low profile gas blocks */
[JsonPropertyName("lowProfileGasBlockTpls")]
@@ -266,10 +266,10 @@ public class WalletLootSettings
public MinMax ItemCount { get; set; }
[JsonPropertyName("stackSizeWeight")]
- public Dictionary StackSizeWeight { get; set; }
+ public Dictionary StackSizeWeight { get; set; }
[JsonPropertyName("currencyWeight")]
- public Dictionary CurrencyWeight { get; set; }
+ public Dictionary CurrencyWeight { get; set; }
///
/// What wallets will have money in them