using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; using Core.Models.Enums; using Core.Models.Utils; using Core.Servers; using Core.Services; using Core.Utils; namespace Core.Helpers; [Injectable] public class BotWeaponGeneratorHelper( ISptLogger _logger, DatabaseServer _databaseServer, ItemHelper _itemHelper, RandomUtil _randomUtil, HashUtil _hashUtil, WeightedRandomHelper _weightedRandomHelper, BotGeneratorHelper _botGeneratorHelper, LocalisationService _localisationService ) { private readonly List _magCheck = ["CylinderMagazine", "SpringDrivenCylinder"]; /// /// Get a randomized number of bullets for a specific magazine /// /// Weights of magazines /// Magazine to generate bullet count for /// Bullet count number public double? GetRandomizedBulletCount(GenerationData magCounts, TemplateItem magTemplate) { var randomizedMagazineCount = GetRandomizedMagazineCount(magCounts); var parentItem = _itemHelper.GetItem(magTemplate.Parent).Value; double? chamberBulletCount = 0; if (MagazineIsCylinderRelated(parentItem.Name)) { var firstSlotAmmoTpl = magTemplate.Properties.Cartridges.FirstOrDefault()?.Props.Filters[0].Filter[0]; var ammoMaxStackSize = _itemHelper.GetItem(firstSlotAmmoTpl).Value?.Properties?.StackMaxSize ?? 1; chamberBulletCount = ammoMaxStackSize == 1 ? 1 // Rotating grenade launcher : magTemplate.Properties.Slots.Count; // Shotguns/revolvers. We count the number of camoras as the _max_count of the magazine is 0 } else if (parentItem.Id == BaseClasses.UBGL) { // Underbarrel launchers can only have 1 chambered grenade chamberBulletCount = 1; } else { chamberBulletCount = magTemplate.Properties.Cartridges?[0].MaxCount; } // Get the amount of bullets that would fit in the internal magazine // and multiply by how many magazines were supposed to be created return chamberBulletCount * randomizedMagazineCount; } /// /// Get a randomized count of magazines /// /// Min and max value returned value can be between /// Numerical value of magazine count public int GetRandomizedMagazineCount(GenerationData magCounts) { return (int)_weightedRandomHelper.GetWeightedValue(magCounts.Weights); } /// /// Is this magazine cylinder related (revolvers and grenade launchers) /// /// The name of the magazines parent /// True if it is cylinder related public bool MagazineIsCylinderRelated(string magazineParentName) { return _magCheck.Contains(magazineParentName); } /// /// Create a magazine using the parameters given /// /// Tpl of the magazine to create /// Ammo to add to magazine /// Template object of magazine /// Item array public List CreateMagazineWithAmmo(string magazineTpl, string ammoTpl, TemplateItem magTemplate) { List magazine = [ new() { Id = _hashUtil.Generate(), Template = magazineTpl } ]; _itemHelper.FillMagazineWithCartridge(magazine, magTemplate, ammoTpl, 1); return magazine; } /// /// Add a specific number of cartridges to a bots inventory (defaults to vest and pockets) /// /// Ammo tpl to add to vest/pockets /// Number of cartridges to add to vest/pockets /// Bot inventory to add cartridges to /// What equipment slots should bullets be added into public void AddAmmoIntoEquipmentSlots( string ammoTpl, int cartridgeCount, BotBaseInventory inventory, List equipmentSlotsToAddTo ) { if (equipmentSlotsToAddTo is null) equipmentSlotsToAddTo = [EquipmentSlots.TacticalVest, EquipmentSlots.Pockets]; var ammoItems = _itemHelper.SplitStack( new Item { Id = _hashUtil.Generate(), Template = ammoTpl, Upd = new Upd { StackObjectsCount = cartridgeCount } } ); foreach (var ammoItem in ammoItems) { var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot( equipmentSlotsToAddTo, ammoItem.Id, ammoItem.Template, [ammoItem], inventory ); if (result != ItemAddedResult.SUCCESS) { _logger.Debug($"Unable to add ammo: {ammoItem.Template} to bot inventory, {result.ToString()}"); if (result == ItemAddedResult.NO_SPACE || result == ItemAddedResult.NO_CONTAINERS) // If there's no space for 1 stack or no containers to hold item, there's no space for the others break; } } } /// /// Get a weapons default magazine template id /// /// Weapon to get default magazine for /// Tpl of magazine public string? GetWeaponsDefaultMagazineTpl(TemplateItem weaponTemplate) { return weaponTemplate.Properties.DefMagType; } }