diff --git a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
index 8a9bec1a..a0ce4565 100644
--- a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
+++ b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs
@@ -416,5 +416,37 @@ namespace SPTarkov.Server.Core.Extensions
return new InventoryItemHash { ByItemId = inventoryItems.ToDictionary(item => item.Id), ByParentId = byParentId };
}
+
+ ///
+ /// Remove spawned in session (FiR) status from items inside a container
+ ///
+ /// Player profile
+ /// Container slot id to find items for and remove FiR from e.g. "Backpack"
+ public static void RemoveFiRStatusFromItemsInContainer(this PmcData pmcData, string containerSlotId)
+ {
+ var container = pmcData?.Inventory?.Items?.FirstOrDefault(item => item.SlotId == containerSlotId);
+ if (container is null)
+ {
+ return;
+ }
+
+ var parentItemLookup = pmcData.Inventory.Items.ToLookup(item => item.ParentId);
+ var parentIdsToSearch = new Queue();
+ parentIdsToSearch.Enqueue(container.Id);
+
+ while (parentIdsToSearch.Count > 0)
+ {
+ var currentParentId = parentIdsToSearch.Dequeue();
+ foreach (var childItem in parentItemLookup[currentParentId])
+ {
+ if (childItem.Upd?.SpawnedInSession != null && childItem.Upd.SpawnedInSession.Value)
+ {
+ childItem.Upd.SpawnedInSession = false;
+ }
+
+ parentIdsToSearch.Enqueue(childItem.Id);
+ }
+ }
+ }
}
}
diff --git a/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs
index f689352d..e9de0b4a 100644
--- a/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs
+++ b/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs
@@ -144,34 +144,6 @@ public class InRaidHelper(InventoryHelper inventoryHelper, ConfigServer configSe
pmcData.Inventory.FastPanel = new();
}
- ///
- /// Remove FiR status from designated container.
- ///
- /// Session id
- /// Player profile
- /// Container slot id to find items for and remove FiR from
- public void RemoveFiRStatusFromItemsInContainer(MongoId sessionId, PmcData pmcData, string secureContainerSlotId)
- {
- if (!pmcData.Inventory.Items.Any(item => item.SlotId == secureContainerSlotId))
- {
- return;
- }
-
- List- itemsInsideContainer = [];
- foreach (var inventoryItem in pmcData.Inventory.Items.Where(item => item.Upd is not null && item.SlotId != "hideout"))
- {
- if (inventoryItem.ItemIsInsideContainer(secureContainerSlotId, pmcData.Inventory.Items))
- {
- itemsInsideContainer.Add(inventoryItem);
- }
- }
-
- foreach (var item in itemsInsideContainer.Where(item => item.Upd?.SpawnedInSession ?? false))
- {
- item.Upd.SpawnedInSession = false;
- }
- }
-
///
/// Get a list of items from a profile that will be lost on death.
///
diff --git a/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs b/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs
index 4f54bf1e..0f6da4f9 100644
--- a/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs
+++ b/Libraries/SPTarkov.Server.Core/Services/LocationLifecycleService.cs
@@ -777,7 +777,7 @@ public class LocationLifecycleService(
inRaidHelper.DeleteInventory(serverPmcProfile, sessionId);
- inRaidHelper.RemoveFiRStatusFromItemsInContainer(sessionId, serverPmcProfile, "SecuredContainer");
+ serverPmcProfile.RemoveFiRStatusFromItemsInContainer("SecuredContainer");
}
// Must occur AFTER killer messages have been sent
diff --git a/UnitTests/Tests/Extensions/ItemTests.cs b/UnitTests/Tests/Extensions/ItemTests.cs
index 960bcd13..20dcd819 100644
--- a/UnitTests/Tests/Extensions/ItemTests.cs
+++ b/UnitTests/Tests/Extensions/ItemTests.cs
@@ -1,6 +1,7 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
+using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
namespace UnitTests.Tests.Extensions;
@@ -139,4 +140,156 @@ public class ItemTests
Assert.AreEqual(result[0].Id, rootItem.Id);
Assert.AreEqual(result.Count, 1);
}
+
+ [Test]
+ public void RemoveFiRStatusFromItemsInContainer_twoItemsInBackpack()
+ {
+ var profile = new PmcData() { Inventory = new BotBaseInventory() { Items = [] } };
+ profile.Inventory.Equipment = new MongoId();
+
+ // Add backpack
+ var backpackId = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = backpackId,
+ Template = ItemTpl.BACKPACK_HAZARD_4_PILLBOX_BACKPACK_BLACK,
+ ParentId = profile.Inventory.Equipment,
+ SlotId = "Backpack",
+ }
+ );
+
+ // Add ifak to first slot in backpack
+ var item1Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item1Id,
+ Template = ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT,
+ ParentId = backpackId,
+ SlotId = "main",
+ Upd = new Upd { SpawnedInSession = true },
+ Location = new ItemLocation
+ {
+ X = 0,
+ Y = 0,
+ R = ItemRotation.Horizontal,
+ },
+ }
+ );
+
+ // Add wrench to first slot of ifak
+ var item2Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item2Id,
+ Template = ItemTpl.BARTER_WRENCH,
+ ParentId = backpackId,
+ SlotId = "main",
+ Upd = new Upd { SpawnedInSession = true },
+ Location = new ItemLocation
+ {
+ X = 1,
+ Y = 0,
+ R = ItemRotation.Horizontal,
+ },
+ }
+ );
+
+ // Add armband to armband slot as root
+ var item3Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item3Id,
+ Template = ItemTpl.ARMBAND_RED,
+ ParentId = profile.Inventory.Equipment,
+ SlotId = "Armband",
+ Upd = new Upd { SpawnedInSession = true },
+ }
+ );
+
+ profile.RemoveFiRStatusFromItemsInContainer("Backpack");
+
+ Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item1Id).Upd.SpawnedInSession);
+ Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item2Id).Upd.SpawnedInSession);
+ Assert.AreEqual(true, profile.Inventory.Items.FirstOrDefault(item => item.Id == item3Id).Upd.SpawnedInSession);
+ }
+
+ [Test]
+ public void RemoveFiRStatusFromItemsInContainer_oneItemWithChildInBackpack()
+ {
+ var profile = new PmcData { Inventory = new BotBaseInventory { Items = [] } };
+ profile.Inventory.Equipment = new MongoId();
+
+ // Add backpack
+ var backpackId = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = backpackId,
+ Template = ItemTpl.BACKPACK_HAZARD_4_PILLBOX_BACKPACK_BLACK,
+ ParentId = profile.Inventory.Equipment,
+ SlotId = "Backpack",
+ }
+ );
+
+ // Add ifak to first slot in backpack
+ var item1Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item1Id,
+ Template = ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT,
+ ParentId = backpackId,
+ SlotId = "main",
+ Upd = new Upd { SpawnedInSession = true },
+ Location = new ItemLocation
+ {
+ X = 0,
+ Y = 0,
+ R = ItemRotation.Horizontal,
+ },
+ }
+ );
+
+ // Add wrench to first slot of ifak as child
+ var item2Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item2Id,
+ Template = ItemTpl.BARTER_WRENCH,
+ ParentId = item1Id,
+ SlotId = "main",
+ Upd = new Upd { SpawnedInSession = true },
+ Location = new ItemLocation
+ {
+ X = 1,
+ Y = 0,
+ R = ItemRotation.Horizontal,
+ },
+ }
+ );
+
+ // Add armband to armband slot as root
+ var item3Id = new MongoId();
+ profile.Inventory.Items.Add(
+ new Item
+ {
+ Id = item3Id,
+ Template = ItemTpl.ARMBAND_RED,
+ ParentId = profile.Inventory.Equipment,
+ SlotId = "Armband",
+ Upd = new Upd { SpawnedInSession = true },
+ }
+ );
+
+ profile.RemoveFiRStatusFromItemsInContainer("Backpack");
+
+ Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item1Id).Upd.SpawnedInSession);
+ Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item2Id).Upd.SpawnedInSession);
+ Assert.AreEqual(true, profile.Inventory.Items.FirstOrDefault(item => item.Id == item3Id).Upd.SpawnedInSession);
+ }
}