diff --git a/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs b/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs index 784fc519..9bd46a80 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs @@ -379,7 +379,7 @@ public class RagfairController( var offers = ragfairOfferService.GetOffersOfType(getPriceRequest.TemplateId); // Offers exist for item, get averages of what's listed - if (offers?.Count > 0) + if (offers.Any()) { // These get calculated while iterating through the list below var minMax = new MinMax(int.MaxValue, 0); @@ -412,7 +412,7 @@ public class RagfairController( } protected double GetAveragePriceFromOffers( - List offers, + IEnumerable offers, MinMax minMax, bool ignoreTraderOffers ) diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotEquipmentModGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotEquipmentModGenerator.cs index 31ccfbd1..c19f48bf 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotEquipmentModGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotEquipmentModGenerator.cs @@ -197,7 +197,8 @@ public class BotEquipmentModGenerator( ); switch (plateSlotFilteringOutcome.Result) { - case Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER: + case Result.UNKNOWN_FAILURE + or Result.NO_DEFAULT_FILTER: if (logger.IsLogEnabled(LogLevel.Debug)) { logger.Debug( @@ -692,6 +693,7 @@ public class BotEquipmentModGenerator( // Force spawn chance to be 100% to ensure it gets added if ( modSlot == "mod_handguard" + && modToAddTemplate.Properties?.Slots is not null && modToAddTemplate.Properties.Slots.Any(slot => slot.Name == "mod_handguard") && !request.Weapon.Any(item => item.SlotId == "mod_launcher") ) @@ -772,6 +774,7 @@ public class BotEquipmentModGenerator( if ( isRandomisableSlot && !containsModInPool + && modToAddTemplate.Properties?.Slots is not null && modToAddTemplate.Properties.Slots.Any() ) { @@ -835,9 +838,15 @@ public class BotEquipmentModGenerator( /// True it lacks cartridges/chamber slots protected bool ItemLacksSlotsCartridgesAndChambers(TemplateItem item) { - return item.Properties.Slots?.Count == 0 - && item.Properties.Cartridges?.Count == 0 - && item.Properties.Chambers?.Count == 0; + if (item.Properties is null) + { + return true; + } + + return item.Properties.Slots is null + || !item.Properties.Slots.Any() + && item.Properties.Cartridges?.Count == 0 + && item.Properties.Chambers?.Count == 0; } /// @@ -854,7 +863,9 @@ public class BotEquipmentModGenerator( ) { // Can the stock hold child items - var hasSubSlots = modToAddTemplate.Properties.Slots?.Count > 0; + var hasSubSlots = + modToAddTemplate.Properties?.Slots is not null + && modToAddTemplate.Properties.Slots.Any(); return (_stockSlots.Contains(modSlot) && hasSubSlots) || botEquipConfig.ForceStock.GetValueOrDefault(false); @@ -1280,8 +1291,8 @@ public class BotEquipmentModGenerator( var desiredMagazineTpls = modPool.Where(magTpl => { var magazineDb = itemHelper.GetItem(magTpl).Value; - return magazineDb.Properties is not null - && magazineDb.Properties.Cartridges.FirstOrDefault().MaxCount + return magazineDb.Properties?.Cartridges is not null + && magazineDb.Properties.Cartridges.FirstOrDefault()?.MaxCount >= minMagSizeFromSettings; }); @@ -1546,7 +1557,7 @@ public class BotEquipmentModGenerator( // Mod isn't in existing pool, only add if it has no children and exists inside parent filter if ( (parentSlotCompatibleItems?.Contains(matchingModFromPreset.Template) ?? false) - && itemHelper.GetItem(matchingModFromPreset.Template).Value.Properties.Slots?.Count == 0 + && !itemHelper.GetItem(matchingModFromPreset.Template).Value.Properties.Slots.Any() ) { // Chosen mod has no conflicts + no children + is in parent compat list @@ -2070,22 +2081,23 @@ 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 HashSet filteredScopesAndMods = []; - foreach (var item in scopes) + foreach (var scopeTpl in scopes) { // Mods is a scope, check base class is allowed - if (itemHelper.IsOfBaseclasses(item, whitelistedSightTypes)) + if (itemHelper.IsOfBaseclasses(scopeTpl, whitelistedSightTypes)) { // Add mod to allowed list - filteredScopesAndMods.Add(item); + filteredScopesAndMods.Add(scopeTpl); continue; } // Edge case, what if item is a mount for a scope and not directly a scope? // Check item is mount + has child items - var itemDetails = itemHelper.GetItem(item).Value; + var itemDetails = itemHelper.GetItem(scopeTpl).Value; if ( - itemHelper.IsOfBaseclass(item, BaseClasses.MOUNT) + itemDetails?.Properties?.Slots is not null && itemDetails.Properties.Slots.Any() + && itemHelper.IsOfBaseclass(scopeTpl, BaseClasses.MOUNT) ) { // Check to see if mount has a scope slot (only include primary slot, ignore the rest like the backup sight slots) @@ -2107,7 +2119,7 @@ public class BotEquipmentModGenerator( ) // Add mod to allowed list { - filteredScopesAndMods.Add(item); + filteredScopesAndMods.Add(scopeTpl); } } } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs index 908b6d99..aec35b16 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotInventoryGenerator.cs @@ -615,7 +615,11 @@ public class BotInventoryGenerator( settings.GenerateModsBlacklist != null && settings.GenerateModsBlacklist.Contains(pickedItemDb.Id); // Does item have slots for sub-mods to be inserted into - if (pickedItemDb.Properties?.Slots?.Count > 0 && !itemIsOnGenerateModBlacklist) + if ( + pickedItemDb.Properties?.Slots is not null + && pickedItemDb.Properties.Slots.Any() + && !itemIsOnGenerateModBlacklist + ) { var childItemsToAdd = botEquipmentModGenerator.GenerateModsForEquipment( [item], diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs index c9d267c2..df64341f 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotLootGenerator.cs @@ -285,7 +285,7 @@ public class BotLootGenerator( var itemPriceLimits = GetSingleItemLootPriceLimits(botLevel, isPmc); // Backpack - generate loot if they have one - if (containersBotHasAvailable.Contains(EquipmentSlots.Backpack)) + if (containersBotHasAvailable.Contains(EquipmentSlots.Backpack) && backpackLootCount > 0) { // Add randomly generated weapon to PMC backpacks if (isPmc && randomUtil.GetChance100(_pmcConfig.LooseWeaponInBackpackChancePercent)) @@ -506,9 +506,7 @@ public class BotLootGenerator( ) { // Loot pool has items - var poolSize = pool.Count; - - if (poolSize <= 0) + if (pool.Count <= 0) { return; } diff --git a/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs index 8093ee96..11b07388 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/BotWeaponGenerator.cs @@ -905,9 +905,9 @@ public class BotWeaponGenerator( /// Weapon with children. /// Underbarrrel grenade launcher item. /// Grenade ammo template. - protected void FillUbgl(IEnumerable weaponMods, Item ubglMod, MongoId ubglAmmoTpl) + protected void FillUbgl(List weaponMods, Item ubglMod, MongoId ubglAmmoTpl) { - weaponMods.Append( + weaponMods.Add( new Item { Id = new MongoId(), diff --git a/Libraries/SPTarkov.Server.Core/Generators/FenceBaseAssortGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/FenceBaseAssortGenerator.cs index 9f33b567..ffc699df 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/FenceBaseAssortGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/FenceBaseAssortGenerator.cs @@ -77,7 +77,7 @@ public class FenceBaseAssortGenerator( // Only allow rigs with no slots (carrier rigs) if ( itemHelper.IsOfBaseclass(itemId, BaseClasses.VEST) - && (rootItemDb.Properties?.Slots?.Count ?? 0) > 0 + && (rootItemDb.Properties?.Slots is not null && rootItemDb.Properties.Slots.Any()) ) { continue; @@ -271,7 +271,8 @@ public class FenceBaseAssortGenerator( protected void AddChildrenToArmorModSlots(List armor, TemplateItem itemDbDetails) { // Armor has no mods, make no additions - var hasMods = itemDbDetails.Properties.Slots.Count > 0; + var hasMods = + itemDbDetails.Properties?.Slots is not null && itemDbDetails.Properties.Slots.Any(); if (!hasMods) { return; diff --git a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs index c9d1a695..64106ba7 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs @@ -171,7 +171,7 @@ public class LocationLootGenerator( // Find all 100% spawn containers var staticLootDist = mapData.StaticLoot; var guaranteedContainers = GetGuaranteedContainers(allStaticContainersOnMapClone); - staticContainerCount += guaranteedContainers.Count; + staticContainerCount += guaranteedContainers.Count(); // Add loot to guaranteed containers and add to result foreach ( @@ -188,12 +188,12 @@ public class LocationLootGenerator( { result.Add(containerWithLoot.Template); - staticLootItemCount += containerWithLoot.Template.Items.Count; + staticLootItemCount += containerWithLoot.Template.Items.Count(); } if (_logger.IsLogEnabled(LogLevel.Debug)) { - _logger.Debug($"Added {guaranteedContainers.Count} guaranteed containers"); + _logger.Debug($"Added {guaranteedContainers.Count()} guaranteed containers"); } // Randomisation is turned off for location / globally @@ -202,7 +202,7 @@ public class LocationLootGenerator( if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug( - $"Container randomisation disabled, Adding: {staticRandomisableContainersOnMap.Count} containers to: {locationId}" + $"Container randomisation disabled, Adding: {staticRandomisableContainersOnMap.Count()} containers to: {locationId}" ); } @@ -217,7 +217,7 @@ public class LocationLootGenerator( ); result.Add(containerWithLoot.Template); - staticLootItemCount += containerWithLoot.Template.Items.Count; + staticLootItemCount += containerWithLoot.Template.Items.Count(); } _logger.Success($"A total of {staticLootItemCount} static items spawned"); @@ -320,7 +320,7 @@ public class LocationLootGenerator( result.Add(containerWithLoot.Template); staticContainerCount++; - staticLootItemCount += containerWithLoot.Template.Items.Count; + staticLootItemCount += containerWithLoot.Template.Items.Count(); } } @@ -346,19 +346,17 @@ public class LocationLootGenerator( /// /// /// StaticContainerData array - protected List GetRandomisableContainersOnMap( - List staticContainers + protected IEnumerable GetRandomisableContainersOnMap( + IEnumerable staticContainers ) { - return staticContainers - .Where(staticContainer => - staticContainer.Probability != 1 - && !staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) - && !_locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains( - staticContainer.Template.Items.FirstOrDefault().Template - ) + return staticContainers.Where(staticContainer => + staticContainer.Probability != 1 + && !staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) + && !_locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains( + staticContainer.Template.Items.FirstOrDefault().Template ) - .ToList(); + ); } /// @@ -366,19 +364,17 @@ public class LocationLootGenerator( /// /// /// IStaticContainerData array - protected List GetGuaranteedContainers( - List staticContainersOnMap + protected IEnumerable GetGuaranteedContainers( + IEnumerable staticContainersOnMap ) { - return staticContainersOnMap - .Where(staticContainer => - staticContainer.Probability == 1 - || staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) - || _locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains( - staticContainer.Template.Items.FirstOrDefault().Template - ) + return staticContainersOnMap.Where(staticContainer => + staticContainer.Probability == 1 + || staticContainer.Template.IsAlwaysSpawn.GetValueOrDefault(false) + || _locationConfig.ContainerRandomisationSettings.ContainerTypesToNotRandomise.Contains( + staticContainer.Template.Items.FirstOrDefault().Template ) - .ToList(); + ); } /// @@ -395,7 +391,7 @@ public class LocationLootGenerator( { var chosenContainerIds = new List(); - var containerIds = containerData.ContainerIdsWithProbability.Keys.ToList(); + var containerIds = containerData.ContainerIdsWithProbability.Keys; if (containerData.ChosenCount > containerIds.Count) { if (_logger.IsLogEnabled(LogLevel.Debug)) @@ -405,7 +401,7 @@ public class LocationLootGenerator( ); } - return containerIds; + return containerIds.ToList(); } // Create probability array with all possible container ids in this group and their relative probability of spawning @@ -429,7 +425,7 @@ public class LocationLootGenerator( /// dictionary keyed by groupId protected Dictionary GetGroupIdToContainerMappings( StaticContainer staticContainerGroupData, - List staticContainersOnMap + IEnumerable staticContainersOnMap ) { // Create dictionary of all group ids and choose a count of containers the map will spawn of that group @@ -532,7 +528,7 @@ public class LocationLootGenerator( /// StaticContainerData protected StaticContainerData AddLootToContainer( StaticContainerData staticContainer, - List? staticForced, + IEnumerable? staticForced, Dictionary staticLootDist, Dictionary> staticAmmoDist, string locationName @@ -639,9 +635,8 @@ public class LocationLootGenerator( }; // Add loot to container before returning - containerClone.Template.Items.AddRange( - items.Select(item => item.ToLootItem()).ToList() // Convert into correct output type first - ); + var itemsToAdd = items.Select(item => item.ToLootItem()); // Convert into correct output type first + containerClone.Template.Items = containerClone.Template.Items.Union(itemsToAdd); } return containerClone; @@ -776,16 +771,12 @@ public class LocationLootGenerator( // Remove christmas items from loot data if (!_seasonalEventService.ChristmasEventEnabled()) { - dynamicLootDist.Spawnpoints = dynamicLootDist - .Spawnpoints.Where(point => - !point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase) - ) - .ToList(); - dynamicLootDist.SpawnpointsForced = dynamicLootDist - .SpawnpointsForced.Where(point => - !point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase) - ) - .ToList(); + dynamicLootDist.Spawnpoints = dynamicLootDist.Spawnpoints.Where(point => + !point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase) + ); + dynamicLootDist.SpawnpointsForced = dynamicLootDist.SpawnpointsForced.Where(point => + !point.Template.Id.StartsWith("christmas", StringComparison.OrdinalIgnoreCase) + ); } // Build the list of forced loot from both `SpawnpointsForced` and any point marked `IsAlwaysSpawn` @@ -929,7 +920,7 @@ public class LocationLootGenerator( } // Spawn point has no items after filtering, skip - if (spawnPoint.Template.Items is null || spawnPoint.Template.Items.Count == 0) + if (spawnPoint.Template.Items is null || !spawnPoint.Template.Items.Any()) { if (_logger.IsLogEnabled(LogLevel.Debug)) { @@ -1031,7 +1022,7 @@ public class LocationLootGenerator( /// /// Collection of spawn points with forced loot in them protected List GetForcedDynamicLoot( - List forcedSpawnPoints, + IEnumerable forcedSpawnPoints, string locationName, Dictionary> staticAmmoDist ) @@ -1105,7 +1096,7 @@ public class LocationLootGenerator( /// ContainerItem object protected ContainerItem CreateDynamicLootItem( SptLootItem chosenItem, - List lootItems, + IEnumerable lootItems, Dictionary> staticAmmoDist ) { @@ -1282,17 +1273,20 @@ public class LocationLootGenerator( var defaultPreset = _presetHelper.GetDefaultPreset(chosenTpl); if (defaultPreset is not null) { - var presetAndModsClone = _cloner.Clone(defaultPreset.Items).ReplaceIDs().ToList(); + var presetAndModsClone = _cloner.Clone(defaultPreset.Items).ReplaceIDs(); presetAndModsClone.RemapRootItemId(); // Use original items parentId otherwise item doesn't get added to container correctly presetAndModsClone.FirstOrDefault().ParentId = rootItem.ParentId; - items = presetAndModsClone; + items = presetAndModsClone.ToList(); } else { // We make base item in calling method, no need to do it here - if ((armorDbTemplate.Properties.Slots?.Count ?? 0) > 0) + if ( + armorDbTemplate.Properties?.Slots is not null + && armorDbTemplate.Properties.Slots.Any() + ) { items = _itemHelper.AddChildSlotItems( items, diff --git a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs index e0b02cc4..efe4f9a8 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs @@ -343,9 +343,9 @@ public class RagfairOfferGenerator( /// Create multiple offers for items by using a unique list of items we've generated previously /// /// Optional, expired offers to regenerate - public void GenerateDynamicOffers(List>? expiredOffers = null) + public void GenerateDynamicOffers(IEnumerable>? expiredOffers = null) { - var replacingExpiredOffers = (expiredOffers?.Count ?? 0) > 0; + var replacingExpiredOffers = expiredOffers is not null && expiredOffers.Any(); var stopwatch = Stopwatch.StartNew(); // get assort items from param if they exist, otherwise grab freshly generated assorts @@ -356,7 +356,7 @@ public class RagfairOfferGenerator( if (logger.IsLogEnabled(LogLevel.Debug) && stopwatch.ElapsedMilliseconds > 0) { logger.Debug( - $"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count} items" + $"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts - {assortItemsToProcess.Count()} items" ); } diff --git a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/ExplorationQuestGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/ExplorationQuestGenerator.cs index 4b6dc3ee..e3996b76 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/ExplorationQuestGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/ExplorationQuestGenerator.cs @@ -209,11 +209,14 @@ public class ExplorationQuestGenerator( /// Map id (e.g. factory4_day) /// Pmc/Scav /// List of Exit objects - protected List? GetLocationExitsForSide(string locationKey, PlayerGroup playerGroup) + protected IEnumerable? GetLocationExitsForSide( + string locationKey, + PlayerGroup playerGroup + ) { var mapExtracts = databaseService.GetLocation(locationKey.ToLowerInvariant())?.AllExtracts; - return mapExtracts?.Where(exit => exit.Side == Enum.GetName(playerGroup)).ToList(); + return mapExtracts?.Where(exit => exit.Side == Enum.GetName(playerGroup)); } /// @@ -308,18 +311,16 @@ public class ExplorationQuestGenerator( } // Only get exits that have a greater than 0% chance to spawn - var exitPool = mapExits.Where(exit => exit.Chance > 0).ToList(); + var exitPool = mapExits.Where(exit => exit.Chance > 0); // Exclude exits with a requirement to leave (e.g. car extracts) - var possibleExits = exitPool - .Where(exit => - repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains( - "PassageRequirement" - ) + var possibleExits = exitPool.Where(exit => + repeatableConfig.QuestConfig.Exploration.SpecificExits.PassageRequirementWhitelist.Contains( + "PassageRequirement" ) - .ToList(); + ); - if (possibleExits.Count == 0) + if (!possibleExits.Any()) { logger.Error( localisationService.GetText( @@ -332,7 +333,7 @@ public class ExplorationQuestGenerator( } // Choose one of the exits we filtered above - var chosenExit = randomUtil.DrawRandomFromList(possibleExits)[0]; + var chosenExit = randomUtil.DrawRandomFromList(possibleExits.ToList())[0]; // Create a quest condition to leave raid via chosen exit var exitCondition = GenerateQuestConditionCounter(chosenExit); diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs index 9a264c77..a765f2c5 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs @@ -596,7 +596,7 @@ public class BotGeneratorHelper( continue; } - if (itemDbDetails?.Properties?.Grids?.Count == 0) + if (itemDbDetails?.Properties?.Grids is null || !itemDbDetails.Properties.Grids.Any()) // Container has no slots to hold items { continue; @@ -611,15 +611,11 @@ public class BotGeneratorHelper( // Iterate over each grid in the container and look for a big enough space for the item to be placed in var currentGridCount = 1; - var totalSlotGridCount = itemDbDetails?.Properties?.Grids?.Count; + var totalSlotGridCount = itemDbDetails?.Properties?.Grids?.Count(); foreach (var slotGrid in itemDbDetails?.Properties?.Grids ?? []) { // Grid is empty, skip or item size is bigger than grid - if ( - slotGrid.Props?.CellsH == 0 - || slotGrid.Props?.CellsV == 0 - || itemWidth * itemHeight > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH - ) + if (IsGridSmallerThanItem(slotGrid, itemWidth, itemHeight)) { continue; } @@ -711,6 +707,13 @@ public class BotGeneratorHelper( return ItemAddedResult.NO_SPACE; } + protected static bool IsGridSmallerThanItem(Grid slotGrid, int itemWidth, int itemHeight) + { + return slotGrid.Props?.CellsH == 0 + || slotGrid.Props?.CellsV == 0 + || itemWidth * itemHeight > slotGrid.Props?.CellsV * slotGrid.Props?.CellsH; + } + /// /// Take a list of items and check if they need children + add them /// diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotWeaponGeneratorHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotWeaponGeneratorHelper.cs index 81d6f95d..8a3124db 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/BotWeaponGeneratorHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/BotWeaponGeneratorHelper.cs @@ -44,7 +44,7 @@ public class BotWeaponGeneratorHelper( 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 + : 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) { diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs index 64f73bd4..44f1f533 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs @@ -369,9 +369,10 @@ public class ItemHelper( { var itemTemplate = GetItem(itemTpl); - return itemTemplate.Value.Properties.Slots.Any(slot => - _removablePlateSlotIds.Contains(slot.Name.ToLowerInvariant()) - ); + return itemTemplate.Value?.Properties?.Slots is not null + && itemTemplate.Value.Properties.Slots.Any(slot => + _removablePlateSlotIds.Contains(slot.Name.ToLowerInvariant()) + ); } /// @@ -543,7 +544,7 @@ public class ItemHelper( { if (databaseService.GetItems().TryGetValue(itemTpl, out var item)) { - return item?.Properties?.Slots?.Count > 0; + return item.Properties?.Slots is not null && item.Properties.Slots.Any(); } return false; @@ -1534,7 +1535,7 @@ public class ItemHelper( magTemplate.Id, BaseClasses.SPRING_DRIVEN_CYLINDER ) - ? magProps?.Slots?.Count // Edge case for rotating grenade launcher magazine + ? magProps?.Slots?.Count() // Edge case for rotating grenade launcher magazine : magProps?.Cartridges?.FirstOrDefault()?.MaxCount; if (magazineCartridgeMaxCount is null) @@ -2001,8 +2002,8 @@ public class ItemHelper( var containerTemplate = GetItem(containerTpl).Value; // Get height/width - var height = containerTemplate.Properties.Grids[0].Props.CellsV; - var width = containerTemplate.Properties.Grids[0].Props.CellsH; + var height = containerTemplate.Properties.Grids.First().Props.CellsV; + var width = containerTemplate.Properties.Grids.First().Props.CellsH; return GetBlankContainerMap(width.Value, height.Value); } diff --git a/Libraries/SPTarkov.Server.Core/Helpers/RagfairOfferHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/RagfairOfferHelper.cs index e482763e..864fd40b 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/RagfairOfferHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/RagfairOfferHelper.cs @@ -225,7 +225,7 @@ public class RagfairOfferHelper( PmcData pmcData ) { - var offersMap = new Dictionary>(); + var offersMap = new Dictionary>(); var offersToReturn = new List(); var playerIsFleaBanned = pmcData.PlayerIsFleaBanned(timeUtil.GetTimeStamp()); var tieredFlea = _ragfairConfig.TieredFlea; diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs index 2f802007..711107e5 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Globals.cs @@ -720,7 +720,7 @@ public record PveSettings [JsonExtensionData] public Dictionary? ExtensionData { get; set; } - public List AvailableVersions { get; set; } + public IEnumerable AvailableVersions { get; set; } public bool ModeEnabled { get; set; } } @@ -730,7 +730,7 @@ public record CoopSettings [JsonExtensionData] public Dictionary? ExtensionData { get; set; } - public List AvailableVersions { get; set; } + public IEnumerable AvailableVersions { get; set; } } public record RunddansSettings @@ -739,7 +739,7 @@ public record RunddansSettings public Dictionary? ExtensionData { get; set; } [JsonPropertyName("accessKeys")] - public List AccessKeys { get; set; } + public IEnumerable AccessKeys { get; set; } [JsonPropertyName("active")] public bool Active { get; set; } @@ -751,7 +751,7 @@ public record RunddansSettings public double ApplyFrozenEverySec { get; set; } [JsonPropertyName("consumables")] - public List Consumables { get; set; } + public IEnumerable Consumables { get; set; } [JsonPropertyName("drunkImmunitySec")] public double DrunkImmunitySec { get; set; } @@ -772,13 +772,13 @@ public record RunddansSettings public double KnifeCritChanceToBreak { get; set; } [JsonPropertyName("locations")] - public List Locations { get; set; } + public IEnumerable Locations { get; set; } [JsonPropertyName("multitoolRepairSec")] public double MultitoolRepairSec { get; set; } [JsonPropertyName("nonExitsLocations")] - public List NonExitsLocations { get; set; } + public IEnumerable NonExitsLocations { get; set; } [JsonPropertyName("rainForFrozen")] public double RainForFrozen { get; set; } @@ -790,7 +790,7 @@ public record RunddansSettings public XY SecToBreak { get; set; } [JsonPropertyName("sleighLocations")] - public List SleighLocations { get; set; } + public IEnumerable SleighLocations { get; set; } } public record SeasonActivity @@ -1627,7 +1627,7 @@ public record CustomizationVoice public string Voice { get; set; } [JsonPropertyName("side")] - public List Side { get; set; } + public IEnumerable Side { get; set; } [JsonPropertyName("isNotRandom")] public bool IsNotRandom { get; set; } @@ -2310,7 +2310,7 @@ public record Buff [JsonPropertyName("SkillName")] public string SkillName { get; set; } - public List AppliesTo { get; set; } + public IEnumerable AppliesTo { get; set; } } public record Tremor @@ -3287,7 +3287,7 @@ public record BTRSettings public Dictionary? ExtensionData { get; set; } [JsonPropertyName("LocationsWithBTR")] - public List LocationsWithBTR { get; set; } + public IEnumerable LocationsWithBTR { get; set; } [JsonPropertyName("BasePriceTaxi")] public double BasePriceTaxi { get; set; } @@ -3422,7 +3422,7 @@ public record PathConfig public string ExitPoint { get; set; } [JsonPropertyName("pathPoints")] - public List PathPoints { get; set; } + public IEnumerable PathPoints { get; set; } [JsonPropertyName("once")] public bool Once { get; set; } @@ -3434,7 +3434,7 @@ public record PathConfig public double CircleCount { get; set; } [JsonPropertyName("skinType")] - public List SkinType { get; set; } + public IEnumerable SkinType { get; set; } } public record SquadSettings @@ -4927,10 +4927,10 @@ public record RepairStrategy public Dictionary? ExtensionData { get; set; } [JsonPropertyName("BuffTypes")] - public List BuffTypes { get; set; } + public IEnumerable BuffTypes { get; set; } [JsonPropertyName("Filter")] - public List Filter { get; set; } + public IEnumerable Filter { get; set; } } public record BotPreset diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Location.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Location.cs index a0fbd2cc..877d3e1b 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Location.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Location.cs @@ -46,7 +46,7 @@ public record Location /// All possible map extracts /// [JsonPropertyName("allExtracts")] - public Exit[] AllExtracts { get; set; } + public IEnumerable AllExtracts { get; set; } } public record StaticContainer @@ -133,10 +133,10 @@ public record StaticContainerDetails public List StaticWeapons { get; set; } [JsonPropertyName("staticContainers")] - public List StaticContainers { get; set; } + public IEnumerable StaticContainers { get; set; } [JsonPropertyName("staticForced")] - public List StaticForced { get; set; } + public IEnumerable StaticForced { get; set; } } public record StaticForced diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/LooseLoot.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/LooseLoot.cs index 6c0f6f9b..cb757d97 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/LooseLoot.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/LooseLoot.cs @@ -12,10 +12,10 @@ public record LooseLoot public SpawnpointCount? SpawnpointCount { get; set; } [JsonPropertyName("spawnpointsForced")] - public List? SpawnpointsForced { get; set; } + public IEnumerable? SpawnpointsForced { get; set; } [JsonPropertyName("spawnpoints")] - public List? Spawnpoints { get; set; } + public IEnumerable? Spawnpoints { get; set; } } public record SpawnpointCount @@ -65,7 +65,7 @@ public record SpawnpointTemplate public bool? IsGroupPosition { get; set; } [JsonPropertyName("GroupPositions")] - public List? GroupPositions { get; set; } + public IEnumerable? GroupPositions { get; set; } [JsonPropertyName("Root")] public string? Root @@ -75,7 +75,7 @@ public record SpawnpointTemplate } [JsonPropertyName("Items")] - public List? Items { get; set; } + public IEnumerable? Items { get; set; } } public record SptLootItem : Item @@ -123,7 +123,7 @@ public record Spawnpoint public SpawnpointTemplate? Template { get; set; } [JsonPropertyName("itemDistribution")] - public List? ItemDistribution { get; set; } + public IEnumerable? ItemDistribution { get; set; } } public record LooseLootItemDistribution diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Item.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Item.cs index 392f437b..faacd170 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Item.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Item.cs @@ -199,7 +199,7 @@ public record LockableComponent [JsonExtensionData] public Dictionary? ExtensionData { get; set; } - public List? KeyIds { get; set; } + public IEnumerable? KeyIds { get; set; } public bool? Locked { get; set; } public LockableKeyComponent? KeyComponent { get; set; } } @@ -327,10 +327,10 @@ public record UpdSight public Dictionary? ExtensionData { get; set; } [JsonPropertyName("ScopesCurrentCalibPointIndexes")] - public List? ScopesCurrentCalibPointIndexes { get; set; } + public IEnumerable? ScopesCurrentCalibPointIndexes { get; set; } [JsonPropertyName("ScopesSelectedModes")] - public List? ScopesSelectedModes { get; set; } + public IEnumerable? ScopesSelectedModes { get; set; } [JsonPropertyName("SelectedScope")] public int? SelectedScope { get; set; } diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/TemplateItem.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/TemplateItem.cs index 80c32146..9557ed4b 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/TemplateItem.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/TemplateItem.cs @@ -353,10 +353,10 @@ public record Props public bool? DogTagQualities { get; set; } [JsonPropertyName("Grids")] - public List? Grids { get; set; } + public IEnumerable? Grids { get; set; } [JsonPropertyName("Slots")] - public List? Slots { get; set; } + public IEnumerable? Slots { get; set; } [JsonPropertyName("CanPutIntoDuringTheRaid")] public bool? CanPutIntoDuringTheRaid { get; set; } diff --git a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs index 0100603e..998e7f39 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs @@ -89,8 +89,8 @@ public class BotEquipmentModPoolService( continue; } - // Skip item without slots - if (item.Properties.Slots is null || item.Properties.Slots.Count == 0) + // No slots + if (item.Properties?.Slots is null || !item.Properties.Slots.Any()) { continue; } @@ -120,7 +120,9 @@ public class BotEquipmentModPoolService( } var subItemDetails = itemHelper.GetItem(itemToAddTpl).Value; - var hasSubItemsToAdd = (subItemDetails?.Properties?.Slots?.Count ?? 0) > 0; + var hasSubItemsToAdd = + subItemDetails.Properties?.Slots is not null + && subItemDetails.Properties.Slots.Any(); // Item has Slots + pool doesn't have value if (hasSubItemsToAdd && !pool.ContainsKey(subItemDetails.Id)) @@ -221,7 +223,7 @@ public class BotEquipmentModPoolService( // Get item from db var itemDb = itemHelper.GetItem(itemTpl).Value; - if (itemDb.Properties.Slots is not null) + if (itemDb.Properties?.Slots is not null) // Loop over slots flagged as 'required' { foreach ( diff --git a/Libraries/SPTarkov.Server.Core/Services/BotWeaponModLimitService.cs b/Libraries/SPTarkov.Server.Core/Services/BotWeaponModLimitService.cs index 80ce2054..c3c38b92 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotWeaponModLimitService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotWeaponModLimitService.cs @@ -122,7 +122,7 @@ public class BotWeaponModLimitService( // Mount has one slot and it is for a mod_scope if ( modLimits.Scope.Count >= modLimits.ScopeMax - && modTemplate.Properties.Slots?.Count == 1 + && modTemplate.Properties?.Slots?.Count() == 1 && itemHelper.IsOfBaseclass(modTemplate.Id, BaseClasses.MOUNT) && !itemHelper.IsOfBaseclass(modsParent.Id, BaseClasses.MOUNT) && modTemplate.Properties.Slots.Any(slot => slot.Name == "mod_scope") @@ -149,7 +149,7 @@ public class BotWeaponModLimitService( // Mod is a mount that can hold only flashlights ad limit is reached (don't want to add empty mounts if limit is reached) if ( modLimits.Scope.Count >= modLimits.ScopeMax - && modTemplate.Properties.Slots?.Count == 1 + && modTemplate.Properties?.Slots?.Count() == 1 && itemHelper.IsOfBaseclass(modTemplate.Id, BaseClasses.MOUNT) && modTemplate.Properties.Slots.Any(slot => slot.Name == "mod_flashlight") ) diff --git a/Libraries/SPTarkov.Server.Core/Services/FenceService.cs b/Libraries/SPTarkov.Server.Core/Services/FenceService.cs index e6b15920..377c1a99 100644 --- a/Libraries/SPTarkov.Server.Core/Services/FenceService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/FenceService.cs @@ -951,7 +951,8 @@ public class FenceService( rootItemBeingAdded.Template, [BaseClasses.ARMORED_EQUIPMENT, BaseClasses.SEARCHABLE_ITEM] ) - && (itemDbDetails.Properties.Slots?.Count ?? 0) > 0; + && itemDbDetails?.Properties?.Slots is not null + && itemDbDetails.Properties.Slots.Any(); // Only one match and it's not medical or armored gear if (matchingItems.Count == 1 && !(isMedical || isGearAndHasSlots)) diff --git a/Libraries/SPTarkov.Server.Core/Services/Mod/CustomItemService.cs b/Libraries/SPTarkov.Server.Core/Services/Mod/CustomItemService.cs index d493bb1e..3411d6f5 100644 --- a/Libraries/SPTarkov.Server.Core/Services/Mod/CustomItemService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/Mod/CustomItemService.cs @@ -288,7 +288,7 @@ public class CustomItemService( protected void AddToWeaponShelf(string newItemId) { // Ids for wall stashes in db - List wallStashIds = + List wallStashIds = [ ItemTpl.HIDEOUTAREACONTAINER_WEAPONSTAND_STASH_1, ItemTpl.HIDEOUTAREACONTAINER_WEAPONSTAND_STASH_2, @@ -299,7 +299,7 @@ public class CustomItemService( var wall = itemHelper.GetItem(wallId); if (wall.Key) { - wall.Value.Properties.Grids[0].Props.Filters[0].Filter.Add(newItemId); + wall.Value.Properties.Grids.First().Props.Filters[0].Filter.Add(newItemId); } } } diff --git a/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs b/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs index 18592bb4..22ec84b5 100644 --- a/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/PostDbLoadService.cs @@ -391,18 +391,21 @@ public class PostDbLoadService( if (existingLootPosition is not null) { - existingLootPosition.Template.Items.AddRange( - positionToAdd.Template.Items - ); - existingLootPosition.ItemDistribution.AddRange( - positionToAdd.ItemDistribution - ); + existingLootPosition.Template.Items = + existingLootPosition.Template.Items.Union( + positionToAdd.Template.Items + ); + + existingLootPosition.ItemDistribution = + existingLootPosition.ItemDistribution.Union( + positionToAdd.ItemDistribution + ); continue; } // New position, add entire object - looselootData.Spawnpoints.Add(positionToAdd); + looselootData.Spawnpoints = looselootData.Spawnpoints.Append(positionToAdd); } return looselootData; diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairLinkedItemService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairLinkedItemService.cs index 2b5ecc9a..5afbb763 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairLinkedItemService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairLinkedItemService.cs @@ -153,7 +153,7 @@ public class RagfairLinkedItemService( var result = new HashSet(); var slots = item.Properties?.Slots; - if (slots is null || slots.Count == 0) + if (slots is null || !slots.Any()) { // No slots, skip return result; diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs index 89964bfd..5c78f2a4 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairOfferService.cs @@ -49,7 +49,7 @@ public class RagfairOfferService( return ragfairOfferHolder.GetOfferById(offerId); } - public List? GetOffersOfType(MongoId templateId) + public IEnumerable? GetOffersOfType(MongoId templateId) { return ragfairOfferHolder.GetOffersByTemplate(templateId); } diff --git a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs index 643b032d..c1ce526e 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs @@ -68,7 +68,7 @@ public class RagfairOfferHolder( /// /// Tpl to get offers for /// RagfairOffer list - public List? GetOffersByTemplate(MongoId templateId) + public IEnumerable? GetOffersByTemplate(MongoId templateId) { // Get the offerIds we want to return if (!_offersByTemplate.TryGetValue(templateId, out var offerIds)) @@ -76,7 +76,7 @@ public class RagfairOfferHolder( return null; } - var result = _offersById.Where(x => offerIds.Contains(x.Key)).Select(x => x.Value).ToList(); + var result = _offersById.Where(x => offerIds.Contains(x.Key)).Select(x => x.Value); return result; } @@ -86,7 +86,7 @@ public class RagfairOfferHolder( /// /// Id of trader to get offers for /// RagfairOffer list - public List GetOffersByTrader(MongoId traderId) + public IEnumerable GetOffersByTrader(MongoId traderId) { if (!_offersByTrader.TryGetValue(traderId, out var offerIds)) { @@ -95,8 +95,7 @@ public class RagfairOfferHolder( return offerIds .Select(offerId => _offersById.GetValueOrDefault(offerId)) - .Where(offer => offer != null) - .ToList(); + .Where(offer => offer != null); } /// @@ -117,7 +116,7 @@ public class RagfairOfferHolder( /// Add a collection of offers to ragfair /// /// Offers to add - public void AddOffers(List offers) + public void AddOffers(IEnumerable offers) { foreach (var offer in offers) { @@ -326,7 +325,7 @@ public class RagfairOfferHolder( /// Get an array of arrays of expired offer items + children /// /// Expired offer assorts - public List> GetExpiredOfferItems() + public IEnumerable> GetExpiredOfferItems() { lock (_expiredOfferIdsLock) { diff --git a/Tools/MongoIdTplGenerator/Generators/ItemTplMongoIdGenerator.cs b/Tools/MongoIdTplGenerator/Generators/ItemTplMongoIdGenerator.cs index f5c3fe15..7feea8e1 100644 --- a/Tools/MongoIdTplGenerator/Generators/ItemTplMongoIdGenerator.cs +++ b/Tools/MongoIdTplGenerator/Generators/ItemTplMongoIdGenerator.cs @@ -1,7 +1,6 @@ using MongoIdTplGenerator.Utils; using SPTarkov.Common.Extensions; using SPTarkov.DI.Annotations; -using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; @@ -498,7 +497,7 @@ public class ItemTplMongoIdGenerator( // Add grid size for lootable containers if (itemHelper.IsOfBaseclass(item.Id, BaseClasses.LOOT_CONTAINER)) { - return $"{item.Properties.Grids[0]?.Props.CellsH}X{item.Properties.Grids[0]?.Props.CellsV}"; + return $"{item.Properties.Grids.First()?.Props.CellsH}X{item.Properties.Grids.First()?.Props.CellsV}"; } // Add ammo caliber to conflicting weapons