diff --git a/Libraries/SPTarkov.Server.Core/Exceptions/Items/ItemHelperException.cs b/Libraries/SPTarkov.Server.Core/Exceptions/Items/ItemHelperException.cs
new file mode 100644
index 00000000..b0f3b065
--- /dev/null
+++ b/Libraries/SPTarkov.Server.Core/Exceptions/Items/ItemHelperException.cs
@@ -0,0 +1,10 @@
+namespace SPTarkov.Server.Core.Exceptions.Items;
+
+public class ItemHelperException : Exception
+{
+ public ItemHelperException(string message)
+ : base(message) { }
+
+ public ItemHelperException(string message, Exception innerException)
+ : base(message, innerException) { }
+}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
index 4056a842..0344752f 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs
@@ -1,5 +1,6 @@
using System.Collections.Frozen;
using SPTarkov.DI.Annotations;
+using SPTarkov.Server.Core.Exceptions.Items;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
@@ -128,7 +129,7 @@ public class ItemHelper(
/// Item tpl to find
/// OPTIONAL - slotId of desired item
/// Item or null if no item found
- public Item GetItemFromPoolByTpl(IEnumerable- itemPool, MongoId tpl, string slotId = "")
+ public Item? GetItemFromPoolByTpl(IEnumerable
- itemPool, MongoId tpl, string slotId = "")
{
// Filter the pool by slotId if provided
var filteredPool = string.IsNullOrEmpty(slotId)
@@ -182,7 +183,7 @@ public class ItemHelper(
Upd itemProperties = new();
// Armors, etc
- if (itemTemplate.Properties.MaxDurability is not null)
+ if (itemTemplate.Properties?.MaxDurability is not null)
{
itemProperties.Repairable = new UpdRepairable
{
@@ -191,43 +192,34 @@ public class ItemHelper(
};
}
- if (itemTemplate.Properties.HasHinge ?? false)
+ if (itemTemplate.Properties?.HasHinge ?? false)
{
itemProperties.Togglable = new UpdTogglable { On = true };
}
- if (itemTemplate.Properties.Foldable ?? false)
+ if (itemTemplate.Properties?.Foldable ?? false)
{
itemProperties.Foldable = new UpdFoldable { Folded = false };
}
- if (itemTemplate.Properties.WeapFireType?.Any() ?? false)
+ if (itemTemplate.Properties?.WeapFireType?.Count == 0)
{
- if (itemTemplate.Properties.WeapFireType.Contains("fullauto"))
- {
- itemProperties.FireMode = new UpdFireMode { FireMode = "fullauto" };
- }
- else
- {
- itemProperties.FireMode = new UpdFireMode { FireMode = randomUtil.GetArrayValue(itemTemplate.Properties.WeapFireType) };
- }
+ itemProperties.FireMode = itemTemplate.Properties.WeapFireType.Contains("fullauto")
+ ? new UpdFireMode { FireMode = "fullauto" }
+ : new UpdFireMode { FireMode = randomUtil.GetArrayValue(itemTemplate.Properties.WeapFireType) };
}
- if (itemTemplate.Properties.MaxHpResource is not null)
+ if (itemTemplate.Properties?.MaxHpResource is not null)
{
itemProperties.MedKit = new UpdMedKit { HpResource = itemTemplate.Properties.MaxHpResource };
}
- if (itemTemplate.Properties.MaxResource is not null && itemTemplate.Properties.FoodUseTime is not null)
+ if (itemTemplate.Properties?.MaxResource is not null && itemTemplate.Properties.FoodUseTime is not null)
{
itemProperties.FoodDrink = new UpdFoodDrink { HpPercent = itemTemplate.Properties.MaxResource };
}
- if (itemTemplate.Parent == BaseClasses.FLASHLIGHT)
- {
- itemProperties.Light = new UpdLight { IsActive = false, SelectedMode = 0 };
- }
- else if (itemTemplate.Parent == BaseClasses.TACTICAL_COMBO)
+ if (itemTemplate.Parent == BaseClasses.FLASHLIGHT || itemTemplate.Parent == BaseClasses.TACTICAL_COMBO)
{
itemProperties.Light = new UpdLight { IsActive = false, SelectedMode = 0 };
}
@@ -238,7 +230,7 @@ public class ItemHelper(
}
// Toggleable face shield
- if ((itemTemplate.Properties.HasHinge ?? false) && (itemTemplate.Properties.FaceShieldComponent ?? false))
+ if ((itemTemplate.Properties?.HasHinge ?? false) && (itemTemplate.Properties.FaceShieldComponent ?? false))
{
itemProperties.Togglable = new UpdTogglable { On = false };
}
@@ -248,11 +240,17 @@ public class ItemHelper(
///
/// Checks if a tpl is a valid item. Valid meaning that it's an item that can be stored in stash
+ ///
/// Valid means:
+ ///
/// Not quest item
+ ///
/// 'Item' type
+ ///
/// Not on the invalid base types array
+ ///
/// Price above 0 roubles
+ ///
///
/// Template id to check
/// OPTIONAL - Base types deemed invalid
@@ -262,6 +260,11 @@ public class ItemHelper(
var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes;
var itemDetails = GetItem(tpl);
+ if (itemDetails.Value is null)
+ {
+ return false;
+ }
+
return itemDetails.Key && IsValidItem(itemDetails.Value, baseTypes);
}
@@ -280,7 +283,7 @@ public class ItemHelper(
{
var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes;
- return !(item.Properties.QuestItem ?? false)
+ return !(item.Properties?.QuestItem ?? false)
&& string.Equals(item.Type, "Item", StringComparison.OrdinalIgnoreCase)
&& GetItemPrice(item.Id) > 0
&& !itemFilterService.IsItemBlacklisted(item.Id)
@@ -346,7 +349,9 @@ public class ItemHelper(
var itemTemplate = GetItem(itemTpl);
return itemTemplate.Value?.Properties?.Slots is not null
- && itemTemplate.Value.Properties.Slots.Any(slot => _removablePlateSlotIds.Contains(slot.Name.ToLowerInvariant()));
+ && itemTemplate.Value.Properties.Slots.Any(slot =>
+ _removablePlateSlotIds.Contains(slot.Name?.ToLowerInvariant() ?? string.Empty)
+ );
}
///
@@ -370,18 +375,13 @@ public class ItemHelper(
}
// Has no slots
- if (!(itemDbDetails.Value.Properties.Slots ?? []).Any())
+ if (!(itemDbDetails.Value?.Properties?.Slots ?? []).Any())
{
return false;
}
// Check if item has slots that match soft insert name ids
- if (itemDbDetails.Value.Properties.Slots.Any(slot => IsSoftInsertId(slot.Name.ToLowerInvariant())))
- {
- return true;
- }
-
- return false;
+ return itemDbDetails.Value?.Properties?.Slots?.Any(slot => IsSoftInsertId(slot.Name?.ToLowerInvariant() ?? string.Empty)) ?? false;
}
///
@@ -481,7 +481,7 @@ public class ItemHelper(
/// Get cloned copy of all item data from items.json
///
/// List of TemplateItem objects
- public List GetItemsClone()
+ public List? GetItemsClone()
{
return cloner.Clone(databaseService.GetItems().Values.ToList());
}
@@ -588,7 +588,7 @@ public class ItemHelper(
if (
skipArmorItemsWithoutDurability
&& IsOfBaseclass(item.Template, BaseClasses.ARMOR)
- && itemDetails?.Properties?.MaxDurability == 0
+ && itemDetails.Properties?.MaxDurability == 0
)
{
return -1;
@@ -599,7 +599,7 @@ public class ItemHelper(
if (item.Upd.MedKit is not null)
{
// Meds
- result = (item.Upd.MedKit.HpResource ?? 0) / (itemDetails.Properties.MaxHpResource ?? 0);
+ result = (item.Upd.MedKit.HpResource ?? 0) / (itemDetails.Properties?.MaxHpResource ?? 0);
}
else if (item.Upd.Repairable is not null)
{
@@ -607,9 +607,9 @@ public class ItemHelper(
}
else if (item.Upd.FoodDrink is not null)
{
- result = (item.Upd.FoodDrink.HpPercent ?? 0) / (itemDetails.Properties.MaxResource ?? 0);
+ result = (item.Upd.FoodDrink.HpPercent ?? 0) / (itemDetails.Properties?.MaxResource ?? 0);
}
- else if (item.Upd.Key?.NumberOfUsages > 0 && itemDetails.Properties.MaximumNumberOfUsage > 0)
+ else if (item.Upd.Key?.NumberOfUsages > 0 && itemDetails.Properties?.MaximumNumberOfUsage > 0)
{
// keys - keys count upwards, not down like everything else
var maxNumOfUsages = itemDetails.Properties.MaximumNumberOfUsage;
@@ -618,11 +618,11 @@ public class ItemHelper(
else if (item.Upd.Resource?.UnitsConsumed > 0)
{
// E.g. fuel tank
- result = (item.Upd.Resource.Value ?? 0) / (itemDetails.Properties.MaxResource ?? 0);
+ result = (item.Upd.Resource.Value ?? 0) / (itemDetails.Properties?.MaxResource ?? 0);
}
else if (item.Upd.RepairKit is not null)
{
- result = (item.Upd.RepairKit.Resource ?? 0) / (itemDetails.Properties.MaxRepairResource ?? 0);
+ result = (item.Upd.RepairKit.Resource ?? 0) / (itemDetails.Properties?.MaxRepairResource ?? 0);
}
if (result == 0)
@@ -732,7 +732,7 @@ public class ItemHelper(
return null;
}
- return item.Properties.StackMaxSize > 1;
+ return item.Properties?.StackMaxSize > 1;
}
///
@@ -742,12 +742,12 @@ public class ItemHelper(
/// List of root item + children.
public List- SplitStack(Item itemToSplit)
{
- if (itemToSplit?.Upd?.StackObjectsCount is null)
+ if (itemToSplit.Upd?.StackObjectsCount is null)
{
return [itemToSplit];
}
- var maxStackSize = GetItem(itemToSplit.Template).Value.Properties.StackMaxSize;
+ var maxStackSize = GetItem(itemToSplit.Template).Value?.Properties?.StackMaxSize;
var remainingCount = itemToSplit.Upd.StackObjectsCount;
List
- rootAndChildren = [];
@@ -755,7 +755,7 @@ public class ItemHelper(
// return the item as is.
if (remainingCount <= maxStackSize)
{
- rootAndChildren.Add(cloner.Clone(itemToSplit));
+ rootAndChildren.Add(cloner.Clone(itemToSplit)!);
return rootAndChildren;
}
@@ -765,8 +765,8 @@ public class ItemHelper(
var amount = Math.Min(remainingCount.Value, maxStackSize ?? 0);
var newStackClone = cloner.Clone(itemToSplit);
- newStackClone.Id = new MongoId();
- newStackClone.Upd.StackObjectsCount = amount;
+ newStackClone!.Id = new MongoId();
+ newStackClone.Upd!.StackObjectsCount = amount;
remainingCount -= amount;
rootAndChildren.Add(newStackClone);
}
@@ -788,7 +788,7 @@ public class ItemHelper(
return [itemWithChildren];
}
- var maxStackSize = GetItem(originRootItem.Template).Value.Properties.StackMaxSize;
+ var maxStackSize = GetItem(originRootItem.Template).Value?.Properties?.StackMaxSize;
var remainingCount = originRootItem.Upd.StackObjectsCount;
List
> result = [];
@@ -804,11 +804,11 @@ public class ItemHelper(
while (remainingCount > 0)
{
// Clone item and make IDs unique
- var itemWithChildrenClone = cloner.Clone(itemWithChildren).ReplaceIDs().ToList();
+ var itemWithChildrenClone = cloner.Clone(itemWithChildren)!.ReplaceIDs().ToList();
// Set stack count to new value
var amount = Math.Min(remainingCount.Value, maxStackSize ?? 0);
- itemWithChildrenClone[0].Upd.StackObjectsCount = amount;
+ itemWithChildrenClone[0].Upd!.StackObjectsCount = amount;
remainingCount -= amount;
result.Add(itemWithChildrenClone);
}
@@ -824,7 +824,7 @@ public class ItemHelper(
public List> SplitStackIntoSeparateItems(Item itemToSplit)
{
var itemTemplate = GetItem(itemToSplit.Template).Value;
- var itemMaxStackSize = itemTemplate.Properties.StackMaxSize ?? 1;
+ var itemMaxStackSize = itemTemplate?.Properties?.StackMaxSize ?? 1;
// item already within bounds of stack size, return it
if (itemToSplit.Upd?.StackObjectsCount <= itemMaxStackSize)
@@ -837,14 +837,14 @@ public class ItemHelper(
// Split items stack into chunks
List> result = [];
- var remainingCount = itemToSplit.Upd.StackObjectsCount;
+ var remainingCount = itemToSplit.Upd?.StackObjectsCount;
while (remainingCount != 0)
{
var amount = Math.Min(remainingCount ?? 0, itemMaxStackSize);
- var newItemClone = cloner.Clone(itemToSplit);
+ var newItemClone = cloner.Clone(itemToSplit)!;
newItemClone.Id = new MongoId();
- newItemClone.Upd.StackObjectsCount = amount;
+ newItemClone.Upd!.StackObjectsCount = amount;
remainingCount -= amount;
result.Add([newItemClone]);
}
@@ -885,7 +885,7 @@ public class ItemHelper(
///
/// Item with mods to update.
/// New id to add on children of base item.
- public void ReplaceRootItemID(IEnumerable- itemWithChildren, MongoId newId)
+ public void ReplaceRootItemId(IEnumerable
- itemWithChildren, MongoId newId)
{
// original id on base item
var oldId = itemWithChildren.First().Id;
@@ -931,6 +931,11 @@ public class ItemHelper(
itemIdBlacklist.UnionWith(insuredItems.Select(x => x.ItemId.Value));
}
+ if (inventory.Items is null)
+ {
+ return;
+ }
+
foreach (var item in inventory.Items)
{
if (itemIdBlacklist.Contains(item.Id))
@@ -948,7 +953,7 @@ public class ItemHelper(
item.Id = newId;
// Find all children of item and update their parent ids to match
- var childItems = inventory.Items.Where(x => x.ParentId == originalId);
+ var childItems = inventory.Items.Where(x => x.ParentId is not null && x.ParentId == originalId);
foreach (var childItem in childItems)
{
childItem.ParentId = newId;
@@ -960,7 +965,7 @@ public class ItemHelper(
continue;
}
- // Update quickslot id
+ // Update quick-slot id
if (inventory.FastPanel.ContainsKey(originalId))
{
inventory.FastPanel[originalId] = newId;
@@ -975,14 +980,8 @@ public class ItemHelper(
/// Items to adjust the IDs of
/// Player profile
/// Insured items that should not have their IDs replaced
- /// Quick slot panel
/// Items
- public IEnumerable
- ReplaceIDs(
- IEnumerable
- originalItems,
- PmcData? pmcData,
- IEnumerable? insuredItems = null,
- Dictionary? fastPanel = null
- )
+ public IEnumerable
- ReplaceIDs(IEnumerable
- originalItems, PmcData? pmcData, IEnumerable? insuredItems = null)
{
// Blacklist
var itemIdBlacklist = new HashSet();
@@ -1033,12 +1032,12 @@ public class ItemHelper(
}
// Also replace in quick slot if the old ID exists.
- if (pmcData.Inventory.FastPanel is null)
+ if (pmcData?.Inventory?.FastPanel is null)
{
continue;
}
- // Update quickslot id
+ // Update quick-slot id
// TODO: i dont think the fast panel key is a mongoid, it should be e.g. "Item4"
if (pmcData.Inventory.FastPanel.ContainsKey(originalId))
{
@@ -1184,7 +1183,7 @@ public class ItemHelper(
while (currentItem != null && IsAttachmentAttached(currentItem))
{
- currentItem = itemsMap.FirstOrDefault(x => x.Key == currentItem.ParentId).Value;
+ currentItem = itemsMap.FirstOrDefault(kvp => kvp.Key == currentItem.ParentId).Value;
if (currentItem == null)
{
return null;
@@ -1203,9 +1202,11 @@ public class ItemHelper(
{
HashSet check = ["hideout", "main"];
+ var slotId = item.SlotId ?? string.Empty;
+
return !(
- check.Contains(item.SlotId) // Is root item
- || _slotsAsStrings.Contains(item.SlotId) // Is root item in equipment slot e.g. `Headwear`
+ check.Contains(slotId) // Is root item
+ || _slotsAsStrings.Contains(slotId) // Is root item in equipment slot e.g. `Headwear`
|| int.TryParse(item.SlotId, out _)
); // Has int as slotId, is inside container. e.g. cartridges
}
@@ -1228,9 +1229,9 @@ public class ItemHelper(
{
var currentItem = itemsMap.GetValueOrDefault(itemId);
- while (currentItem is not null && !_slotsAsStrings.Contains(currentItem.SlotId))
+ while (currentItem is not null && !_slotsAsStrings.Contains(currentItem.SlotId ?? string.Empty))
{
- currentItem = itemsMap.GetValueOrDefault(currentItem.ParentId);
+ currentItem = itemsMap.GetValueOrDefault(currentItem.ParentId ?? string.Empty);
if (currentItem is null)
{
return null;
@@ -1246,11 +1247,22 @@ public class ItemHelper(
/// Item with children
/// The base items root id
/// ItemSize object (width and height)
- public ItemSize GetItemSize(ICollection
- items, MongoId rootItemId)
+ public ItemSize? GetItemSize(ICollection
- items, MongoId rootItemId)
{
- var rootTemplate = GetItem(items.FirstOrDefault(x => x.Id == rootItemId).Template).Value;
- var width = rootTemplate.Properties.Width;
- var height = rootTemplate.Properties.Height;
+ var itemTemplate = items.FirstOrDefault(x => x.Id == rootItemId)?.Template;
+ if (itemTemplate is null)
+ {
+ return null;
+ }
+
+ var rootTemplate = GetItem(itemTemplate.Value).Value;
+ if (rootTemplate is null)
+ {
+ return null;
+ }
+
+ var width = rootTemplate.Properties?.Width;
+ var height = rootTemplate.Properties?.Height;
var sizeUp = 0;
var sizeDown = 0;
@@ -1268,7 +1280,7 @@ public class ItemHelper(
var itemDbTemplate = GetItem(item.Template).Value;
// Calculating child ExtraSize
- if (itemDbTemplate.Properties.ExtraSizeForceAdd ?? false)
+ if (itemDbTemplate?.Properties?.ExtraSizeForceAdd ?? false)
{
forcedUp += itemDbTemplate.Properties.ExtraSizeUp.Value;
forcedDown += itemDbTemplate.Properties.ExtraSizeDown.Value;
@@ -1277,11 +1289,11 @@ public class ItemHelper(
}
else
{
- sizeUp = sizeUp < itemDbTemplate.Properties.ExtraSizeUp ? itemDbTemplate.Properties.ExtraSizeUp.Value : sizeUp;
- sizeDown = sizeDown < itemDbTemplate.Properties.ExtraSizeDown ? itemDbTemplate.Properties.ExtraSizeDown.Value : sizeDown;
- sizeLeft = sizeLeft < itemDbTemplate.Properties.ExtraSizeLeft ? itemDbTemplate.Properties.ExtraSizeLeft.Value : sizeLeft;
+ sizeUp = sizeUp < itemDbTemplate?.Properties?.ExtraSizeUp ? itemDbTemplate.Properties.ExtraSizeUp.Value : sizeUp;
+ sizeDown = sizeDown < itemDbTemplate?.Properties?.ExtraSizeDown ? itemDbTemplate.Properties.ExtraSizeDown.Value : sizeDown;
+ sizeLeft = sizeLeft < itemDbTemplate?.Properties?.ExtraSizeLeft ? itemDbTemplate.Properties.ExtraSizeLeft.Value : sizeLeft;
sizeRight =
- sizeRight < itemDbTemplate.Properties.ExtraSizeRight ? itemDbTemplate.Properties.ExtraSizeRight.Value : sizeRight;
+ sizeRight < itemDbTemplate?.Properties?.ExtraSizeRight ? itemDbTemplate.Properties.ExtraSizeRight.Value : sizeRight;
}
}
@@ -1316,10 +1328,10 @@ public class ItemHelper(
/// Item template from items db
public void AddCartridgesToAmmoBox(List
- ammoBox, TemplateItem ammoBoxDetails)
{
- var ammoBoxMaxCartridgeCount = ammoBoxDetails.Properties.StackSlots.First().MaxCount;
- var cartridgeTpl = ammoBoxDetails.Properties.StackSlots.First().Props.Filters.First().Filter.FirstOrDefault();
- var cartridgeDetails = GetItem(cartridgeTpl);
- var cartridgeMaxStackSize = cartridgeDetails.Value.Properties.StackMaxSize;
+ var ammoBoxMaxCartridgeCount = ammoBoxDetails.Properties?.StackSlots?.First().MaxCount;
+ var cartridgeTpl = ammoBoxDetails.Properties?.StackSlots?.First().Props?.Filters?.First().Filter?.FirstOrDefault();
+ var cartridgeDetails = GetItem(cartridgeTpl.Value);
+ var cartridgeMaxStackSize = cartridgeDetails.Value?.Properties?.StackMaxSize;
// Exit early if ammo already exists in box
if (ammoBox.Any(item => item.Template.Equals(cartridgeTpl)))
@@ -1339,7 +1351,7 @@ public class ItemHelper(
var cartridgeCountToAdd = remainingSpace < maxPerStack ? remainingSpace : maxPerStack;
// Add cartridge item into items array
- var cartridgeItemToAdd = CreateCartridges(ammoBox[0].Id, cartridgeTpl, (int)cartridgeCountToAdd, location);
+ var cartridgeItemToAdd = CreateCartridges(ammoBox[0].Id, cartridgeTpl.Value, (int)cartridgeCountToAdd, location);
// In live no ammo box has the first cartridge item with a location
if (location == 0)
@@ -1387,11 +1399,14 @@ public class ItemHelper(
)
{
var chosenCaliber = caliber ?? GetRandomValidCaliber(magTemplate);
-
- // Edge case - Klin pp-9 has a typo in its ammo caliber
- if (chosenCaliber == "Caliber9x18PMM")
+ switch (chosenCaliber)
{
- chosenCaliber = "Caliber9x18PM";
+ case null:
+ throw new ItemHelperException("Chosen caliber is null when trying to fill magazine with random cartridge");
+ // Edge case - Klin pp-9 has a typo in its ammo caliber
+ case "Caliber9x18PMM":
+ chosenCaliber = "Caliber9x18PM";
+ break;
}
// Chose a randomly weighted cartridge that fits
@@ -1405,7 +1420,7 @@ public class ItemHelper(
{
if (logger.IsLogEnabled(LogLevel.Debug))
{
- logger.Debug($"Unable to fill item: {magazine.FirstOrDefault().Id} {magTemplate.Name} with cartridges, none found.");
+ logger.Debug($"Unable to fill item: {magazine.FirstOrDefault()?.Id} {magTemplate.Name} with cartridges, none found.");
}
return;
@@ -1428,8 +1443,8 @@ public class ItemHelper(
double minSizeMultiplier = 0.25
)
{
- var isUBGL = IsOfBaseclass(magTemplate.Id, BaseClasses.LAUNCHER);
- if (isUBGL)
+ var isUbgl = IsOfBaseclass(magTemplate.Id, BaseClasses.LAUNCHER);
+ if (isUbgl)
// UBGL don't have mags
{
return;
@@ -1509,8 +1524,13 @@ public class ItemHelper(
/// Tpl of cartridge
protected string? GetRandomValidCaliber(TemplateItem magTemplate)
{
- var ammoTpls = magTemplate.Properties.Cartridges.First().Props.Filters.First().Filter;
- var calibers = ammoTpls.Where(x => GetItem(x).Key).Select(x => GetItem(x).Value.Properties.Caliber).ToList();
+ var ammoTpls = magTemplate.Properties?.Cartridges?.First().Props?.Filters?.First().Filter;
+ var calibers = ammoTpls?.Where(x => GetItem(x).Key).Select(x => GetItem(x).Value?.Properties?.Caliber).ToList();
+
+ if (calibers is null)
+ {
+ throw new ItemHelperException("Calibers is null when trying to generate random valid caliber");
+ }
return randomUtil.DrawRandomFromList(calibers).FirstOrDefault();
}
@@ -1548,16 +1568,22 @@ public class ItemHelper(
}
var ammoArray = new ProbabilityObjectArray(cloner);
- foreach (var icd in ammos)
+ foreach (var ammoDetails in ammos)
{
+ if (ammoDetails.Tpl is null)
+ {
+ logger.Error("Ammo details tpl is null when trying to draw ammo from pool");
+ continue;
+ }
+
// Whitelist exists and tpl not inside it, skip
// Fixes 9x18mm kedr issues
- if (cartridgeWhitelist is not null && !cartridgeWhitelist.Contains(icd.Tpl.Value))
+ if (cartridgeWhitelist is not null && !cartridgeWhitelist.Contains(ammoDetails.Tpl.Value))
{
continue;
}
- ammoArray.Add(new ProbabilityObject(icd.Tpl.Value, (double)icd.RelativeProbability, null));
+ ammoArray.Add(new ProbabilityObject(ammoDetails.Tpl.Value, (double)ammoDetails.RelativeProbability, null));
}
return ammoArray.Draw().FirstOrDefault();
@@ -1635,7 +1661,7 @@ public class ItemHelper(
{
var result = itemToAdd;
HashSet incompatibleModTpls = [];
- foreach (var slot in itemToAddTemplate.Properties.Slots)
+ foreach (var slot in itemToAddTemplate.Properties?.Slots ?? [])
{
// If only required mods is requested, skip non-essential
if (requiredOnly && !(slot.Required ?? false))
@@ -1647,16 +1673,16 @@ public class ItemHelper(
if (modSpawnChanceDict is not null && !(slot.Required ?? false))
{
// only roll chance to not include mod if dict exists and has value for this mod type (e.g. front_plate)
- if (modSpawnChanceDict.ContainsKey(slot.Name.ToLowerInvariant()))
+ if (modSpawnChanceDict.TryGetValue(slot.Name?.ToLowerInvariant() ?? string.Empty, out var value))
{
- if (!randomUtil.GetChance100(modSpawnChanceDict[slot.Name.ToLowerInvariant()]))
+ if (!randomUtil.GetChance100(value))
{
continue;
}
}
}
- var itemPool = slot.Props.Filters.FirstOrDefault().Filter ?? [];
+ var itemPool = slot.Props?.Filters?.FirstOrDefault()?.Filter ?? [];
if (itemPool.Count == 0)
{
if (logger.IsLogEnabled(LogLevel.Debug))
@@ -1695,6 +1721,10 @@ public class ItemHelper(
result.Add(modItemToAdd);
var modItemDbDetails = GetItem(modItemToAdd.Template).Value;
+ if (modItemDbDetails?.Properties?.ConflictingItems is null)
+ {
+ continue;
+ }
// Include conflicting items of newly added mod in pool to be used for next mod choice
incompatibleModTpls.UnionWith(modItemDbDetails.Properties.ConflictingItems);
@@ -1842,8 +1872,13 @@ public class ItemHelper(
var containerTemplate = GetItem(containerTpl).Value;
// Get height/width
- var height = containerTemplate.Properties.Grids.First().Props.CellsV;
- var width = containerTemplate.Properties.Grids.First().Props.CellsH;
+ var height = containerTemplate?.Properties?.Grids?.First().Props?.CellsV;
+ var width = containerTemplate?.Properties?.Grids?.First().Props?.CellsH;
+
+ if (height is null || width is null)
+ {
+ throw new ItemHelperException("Height or width is null when trying to calculate container mapping");
+ }
return GetBlankContainerMap(width.Value, height.Value);
}