diff --git a/Content.Shared/Storage/Components/MaterialReclaimerMagnetPickupComponent.cs b/Content.Shared/Storage/Components/MaterialReclaimerMagnetPickupComponent.cs
new file mode 100644
index 00000000000..86c3bb83fb8
--- /dev/null
+++ b/Content.Shared/Storage/Components/MaterialReclaimerMagnetPickupComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Tag;
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Storage.Components;
+
+///
+/// Applies an ongoing pickup area around the attached entity.
+///
+[RegisterComponent]
+public sealed partial class MaterialReclaimerMagnetPickupComponent : Component
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("nextScan")]
+ public TimeSpan NextScan = TimeSpan.Zero;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("range")]
+ public float Range = 1f;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("whitelist")]
+ public EntityWhitelist? Whitelist;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("blacklist")]
+ public EntityWhitelist? Blacklist;
+}
diff --git a/Content.Shared/Storage/Components/MaterialStorageMagnetPickupComponent.cs b/Content.Shared/Storage/Components/MaterialStorageMagnetPickupComponent.cs
new file mode 100644
index 00000000000..e2ced955e8d
--- /dev/null
+++ b/Content.Shared/Storage/Components/MaterialStorageMagnetPickupComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Tag;
+using Content.Shared.Whitelist;
+
+namespace Content.Server.Storage.Components;
+
+///
+/// Applies an ongoing pickup area around the attached entity.
+///
+[RegisterComponent]
+public sealed partial class MaterialStorageMagnetPickupComponent : Component
+{
+ [ViewVariables(VVAccess.ReadWrite), DataField("nextScan")]
+ public TimeSpan NextScan = TimeSpan.Zero;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("range")]
+ public float Range = 1f;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("whitelist")]
+ public EntityWhitelist? Whitelist;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("blacklist")]
+ public EntityWhitelist? Blacklist;
+}
diff --git a/Content.Shared/Storage/EntitySystems/MaterialReclaimerMagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MaterialReclaimerMagnetPickupSystem.cs
new file mode 100644
index 00000000000..59002fd998a
--- /dev/null
+++ b/Content.Shared/Storage/EntitySystems/MaterialReclaimerMagnetPickupSystem.cs
@@ -0,0 +1,74 @@
+using Content.Server.Storage.Components;
+using Content.Shared.Materials;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Storage.EntitySystems;
+
+///
+///
+///
+public sealed class MaterialReclaimerMagnetPickupSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMaterialReclaimerSystem _storage = default!;
+
+ private static readonly TimeSpan ScanDelay = TimeSpan.FromSeconds(1);
+
+ private EntityQuery _physicsQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _physicsQuery = GetEntityQuery();
+ SubscribeLocalEvent(OnMagnetMapInit);
+ SubscribeLocalEvent(OnMagnetUnpaused);
+ }
+
+ private void OnMagnetUnpaused(EntityUid uid, MaterialReclaimerMagnetPickupComponent component, ref EntityUnpausedEvent args)
+ {
+ component.NextScan += args.PausedTime;
+ }
+
+ private void OnMagnetMapInit(EntityUid uid, MaterialReclaimerMagnetPickupComponent component, MapInitEvent args)
+ {
+ component.NextScan = _timing.CurTime;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ var currentTime = _timing.CurTime;
+ var query = EntityQueryEnumerator();
+
+ while (query.MoveNext(out var uid, out var comp, out var storage, out var xform))
+ {
+ if (comp.NextScan < currentTime)
+ continue;
+
+ comp.NextScan += ScanDelay;
+
+ var parentUid = xform.ParentUid;
+
+ foreach (var near in _lookup.GetEntitiesInRange(uid, comp.Range, LookupFlags.Dynamic | LookupFlags.Sundries))
+ {
+ if (comp.Blacklist is { } blacklist && blacklist.IsValid(near, EntityManager) == true)
+ continue;
+
+ if (comp.Whitelist is { } whitelist && whitelist.IsValid(near, EntityManager) == false)
+ continue;
+
+ if (!_physicsQuery.TryGetComponent(near, out var physics) || physics.BodyStatus != BodyStatus.OnGround)
+ continue;
+
+ if (near == parentUid)
+ continue;
+
+ if (!_storage.TryStartProcessItem(uid, near))
+ continue;
+ }
+ }
+ }
+}
diff --git a/Content.Shared/Storage/EntitySystems/MaterialStorageMagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MaterialStorageMagnetPickupSystem.cs
new file mode 100644
index 00000000000..9931592c1eb
--- /dev/null
+++ b/Content.Shared/Storage/EntitySystems/MaterialStorageMagnetPickupSystem.cs
@@ -0,0 +1,74 @@
+using Content.Server.Storage.Components;
+using Content.Shared.Materials;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Storage.EntitySystems;
+
+///
+///
+///
+public sealed class MaterialStorageMagnetPickupSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMaterialStorageSystem _storage = default!;
+
+ private static readonly TimeSpan ScanDelay = TimeSpan.FromSeconds(1);
+
+ private EntityQuery _physicsQuery;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ _physicsQuery = GetEntityQuery();
+ SubscribeLocalEvent(OnMagnetMapInit);
+ SubscribeLocalEvent(OnMagnetUnpaused);
+ }
+
+ private void OnMagnetUnpaused(EntityUid uid, MaterialStorageMagnetPickupComponent component, ref EntityUnpausedEvent args)
+ {
+ component.NextScan += args.PausedTime;
+ }
+
+ private void OnMagnetMapInit(EntityUid uid, MaterialStorageMagnetPickupComponent component, MapInitEvent args)
+ {
+ component.NextScan = _timing.CurTime;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ var currentTime = _timing.CurTime;
+ var query = EntityQueryEnumerator();
+
+ while (query.MoveNext(out var uid, out var comp, out var storage, out var xform))
+ {
+ if (comp.NextScan < currentTime)
+ continue;
+
+ comp.NextScan += ScanDelay;
+
+ var parentUid = xform.ParentUid;
+
+ foreach (var near in _lookup.GetEntitiesInRange(uid, comp.Range, LookupFlags.Dynamic | LookupFlags.Sundries))
+ {
+ if (comp.Blacklist is { } blacklist && blacklist.IsValid(near, EntityManager) == true)
+ continue;
+
+ if (comp.Whitelist is { } whitelist && whitelist.IsValid(near, EntityManager) == false)
+ continue;
+
+ if (!_physicsQuery.TryGetComponent(near, out var physics) || physics.BodyStatus != BodyStatus.OnGround)
+ continue;
+
+ if (near == parentUid)
+ continue;
+
+ if (!_storage.TryInsertMaterialEntity(uid, near, uid, storage))
+ continue;
+ }
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
index fa72e2f66fe..7f6abe1df9f 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml
@@ -841,6 +841,11 @@
- IngotGold30
- IngotSilver30
- MaterialBananium10
+ - type: MaterialStorageMagnetPickup
+ range: 0.30
+ whitelist:
+ tags:
+ - Ore
- type: entity
parent: BaseLathe
diff --git a/Resources/Prototypes/Entities/Structures/Machines/material_reclaimer.yml b/Resources/Prototypes/Entities/Structures/Machines/material_reclaimer.yml
index bb8f6d200b7..934ee3c58c2 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/material_reclaimer.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/material_reclaimer.yml
@@ -100,3 +100,19 @@
solution: output
- type: StaticPrice
price: 500
+ - type: MaterialReclaimerMagnetPickup
+ range: 0.30
+ whitelist:
+ components:
+ - PhysicalComposition
+ - SpaceGarbage
+ tags:
+ - Trash
+ - Recyclable
+ blacklist:
+ components:
+ - Material
+ - Pda
+ - IdCard
+ tags:
+ - HighRiskItem