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