diff --git a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs index 5f7f8e0056..dd013ed235 100644 --- a/Content.Client/Forensics/ForensicScannerMenu.xaml.cs +++ b/Content.Client/Forensics/ForensicScannerMenu.xaml.cs @@ -54,10 +54,16 @@ namespace Content.Client.Forensics } text.AppendLine(); text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas")); - foreach (var dna in msg.DNAs) + foreach (var dna in msg.TouchDNAs) { text.AppendLine(dna); } + foreach (var dna in msg.SolutionDNAs) + { + if (msg.TouchDNAs.Contains(dna)) + continue; + text.AppendLine(dna); + } text.AppendLine(); text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); foreach (var residue in msg.Residues) diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index a8a86028d4..9647634a9e 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -8,10 +8,12 @@ using Content.Shared.Alert; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Drunk; using Content.Shared.FixedPoint; +using Content.Shared.Forensics; using Content.Shared.HealthExaminable; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; @@ -54,6 +56,7 @@ public sealed class BloodstreamSystem : EntitySystem SubscribeLocalEvent(OnReactionAttempt); SubscribeLocalEvent>(OnReactionAttempt); SubscribeLocalEvent(OnRejuvenate); + SubscribeLocalEvent(OnDnaGenerated); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -183,8 +186,18 @@ public sealed class BloodstreamSystem : EntitySystem bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume; tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well + // Ensure blood that should have DNA has it; must be run here, in case DnaComponent has not yet been initialized + + if (TryComp(entity.Owner, out var donorComp) && donorComp.DNA == String.Empty) + { + donorComp.DNA = _forensicsSystem.GenerateDNA(); + + var ev = new GenerateDnaEvent { Owner = entity.Owner, DNA = donorComp.DNA }; + RaiseLocalEvent(entity.Owner, ref ev); + } + // Fill blood solution with BLOOD - bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume); + bloodSolution.AddReagent(new ReagentId(entity.Comp.BloodReagent, GetEntityBloodData(entity.Owner)), entity.Comp.BloodMaxVolume - bloodSolution.Volume); } private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) @@ -349,7 +362,7 @@ public sealed class BloodstreamSystem : EntitySystem } if (amount >= 0) - return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _); + return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, null, GetEntityBloodData(uid)); // Removal is more involved, // since we also wanna handle moving it to the temporary solution @@ -370,10 +383,7 @@ public sealed class BloodstreamSystem : EntitySystem tempSolution.AddSolution(temp, _prototypeManager); } - if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false)) - { - _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); - } + _puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false); tempSolution.RemoveAllSolution(); } @@ -436,10 +446,7 @@ public sealed class BloodstreamSystem : EntitySystem _solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value); } - if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid)) - { - _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); - } + _puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid); } /// @@ -464,6 +471,40 @@ public sealed class BloodstreamSystem : EntitySystem component.BloodReagent = reagent; if (currentVolume > 0) - _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, out _); + _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, null, GetEntityBloodData(uid)); + } + + private void OnDnaGenerated(Entity entity, ref GenerateDnaEvent args) + { + if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)) + { + foreach (var reagent in bloodSolution.Contents) + { + List reagentData = reagent.Reagent.EnsureReagentData(); + reagentData.RemoveAll(x => x is DnaData); + reagentData.AddRange(GetEntityBloodData(entity.Owner)); + } + } + } + + /// + /// Get the reagent data for blood that a specific entity should have. + /// + public List GetEntityBloodData(EntityUid uid) + { + var bloodData = new List(); + var dnaData = new DnaData(); + + if (TryComp(uid, out var donorComp)) + { + dnaData.DNA = donorComp.DNA; + } else + { + dnaData.DNA = Loc.GetString("forensics-dna-unknown"); + } + + bloodData.Add(dnaData); + + return bloodData; } } diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index dbb8572299..5093b59409 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -123,7 +123,7 @@ namespace Content.Server.Chemistry.EntitySystems var reagent = _protoManager.Index(reagentQuantity.Reagent.Prototype); var reaction = - reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager); + reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager, reagentQuantity.Reagent.Data); if (reaction > reagentQuantity.Quantity) { diff --git a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs index 6958dabb81..5047a26052 100644 --- a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs @@ -21,10 +21,12 @@ public sealed partial class CleanDecalsReaction : ITileReaction [DataField] public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f); + public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { if (reactVolume <= CleanCost || !entityManager.TryGetComponent(tile.GridUid, out var grid) || diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index 08f77de72d..ad02cb8ff9 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -34,7 +34,8 @@ public sealed partial class CleanTileReaction : ITileReaction FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager + , List? data) { var entities = entityManager.System().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); var puddleQuery = entityManager.GetEntityQuery(); diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 6b106b1fc0..0249b6255a 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -38,7 +38,8 @@ public sealed partial class CreateEntityTileReaction : ITileReaction public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { if (reactVolume >= Usage) { diff --git a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs index 198f650ac1..2b9475235f 100644 --- a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs @@ -17,7 +17,8 @@ namespace Content.Server.Chemistry.TileReactions public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; diff --git a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs index b13b70d3d5..dd0b4960ef 100644 --- a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs @@ -16,7 +16,8 @@ namespace Content.Server.Chemistry.TileReactions public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; diff --git a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs index c10b031720..49971475c1 100644 --- a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs @@ -1,4 +1,4 @@ -using Content.Server.Maps; +using Content.Server.Maps; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; @@ -15,7 +15,8 @@ public sealed partial class PryTileReaction : ITileReaction public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { var sys = entityManager.System(); sys.PryTile(tile); diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index 6b46b89495..8c8b371da3 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -15,13 +15,14 @@ namespace Content.Server.Chemistry.TileReactions public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { var spillSystem = entityManager.System(); if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) return FixedPoint2.Zero; - return spillSystem.TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out _, sound: false, tileReact: false) + return spillSystem.TrySpillAt(tile, new Solution(reagent.ID, reactVolume, data), out _, sound: false, tileReact: false) ? reactVolume : FixedPoint2.Zero; } diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index fadc7147c9..f9fb2b90d0 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -29,13 +29,14 @@ namespace Content.Server.Chemistry.TileReactions public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager) + IEntityManager entityManager, + List? data) { if (reactVolume < 5) return FixedPoint2.Zero; if (entityManager.EntitySysManager.GetEntitySystem() - .TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out var puddleUid, false, false)) + .TrySpillAt(tile, new Solution(reagent.ID, reactVolume, data), out var puddleUid, false, false)) { var slippery = entityManager.EnsureComponent(puddleUid); slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier; diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index a232ed8c0e..67ba2a76bb 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -712,7 +712,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem { var (reagent, quantity) = solution.Contents[i]; var proto = _prototypeManager.Index(reagent.Prototype); - var removed = proto.ReactionTile(tileRef, quantity, EntityManager); + var removed = proto.ReactionTile(tileRef, quantity, EntityManager, reagent.Data); if (removed <= FixedPoint2.Zero) continue; diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index 78693bfdbc..72450562f1 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -315,7 +315,7 @@ public sealed class SmokeSystem : EntitySystem continue; var reagent = _prototype.Index(reagentQuantity.Reagent.Prototype); - reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager); + reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager, reagentQuantity.Reagent.Data); } } diff --git a/Content.Server/Forensics/Components/DnaSubstanceTraceComponent.cs b/Content.Server/Forensics/Components/DnaSubstanceTraceComponent.cs new file mode 100644 index 0000000000..7b3cb978f2 --- /dev/null +++ b/Content.Server/Forensics/Components/DnaSubstanceTraceComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Forensics; + +/// +/// This component stops the entity from leaving finger prints, +/// usually so fibres can be left instead. +/// +[RegisterComponent] +public sealed partial class DnaSubstanceTraceComponent : Component +{ } diff --git a/Content.Server/Forensics/Components/ForensicScannerComponent.cs b/Content.Server/Forensics/Components/ForensicScannerComponent.cs index ad26213848..033ea913c3 100644 --- a/Content.Server/Forensics/Components/ForensicScannerComponent.cs +++ b/Content.Server/Forensics/Components/ForensicScannerComponent.cs @@ -26,7 +26,13 @@ namespace Content.Server.Forensics /// DNA that the forensic scanner found from the on an entity. /// [ViewVariables(VVAccess.ReadOnly), DataField("dnas")] - public List DNAs = new(); + public List TouchDNAs = new(); + + /// + /// DNA that the forensic scanner found from the solution containers in an entity. + /// + [ViewVariables(VVAccess.ReadOnly), DataField] + public List SolutionDNAs = new(); /// /// Residue that the forensic scanner found from the on an entity. diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index 80062569b8..b6dc068f78 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -3,16 +3,19 @@ using System.Text; using Content.Server.Popups; using Content.Shared.UserInterface; using Content.Shared.DoAfter; +using Content.Shared.Fluids.Components; using Content.Shared.Forensics; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Paper; using Content.Shared.Verbs; +using Content.Shared.Tag; using Robust.Shared.Audio.Systems; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Timing; +using Content.Server.Chemistry.Containers.EntitySystems; // todo: remove this stinky LINQy namespace Content.Server.Forensics @@ -27,6 +30,9 @@ namespace Content.Server.Forensics [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -46,7 +52,8 @@ namespace Content.Server.Forensics var state = new ForensicScannerBoundUserInterfaceState( component.Fingerprints, component.Fibers, - component.DNAs, + component.TouchDNAs, + component.SolutionDNAs, component.Residues, component.LastScannedName, component.PrintCooldown, @@ -69,18 +76,25 @@ namespace Content.Server.Forensics { scanner.Fingerprints = new(); scanner.Fibers = new(); - scanner.DNAs = new(); + scanner.TouchDNAs = new(); scanner.Residues = new(); } - else { scanner.Fingerprints = forensics.Fingerprints.ToList(); scanner.Fibers = forensics.Fibers.ToList(); - scanner.DNAs = forensics.DNAs.ToList(); + scanner.TouchDNAs = forensics.DNAs.ToList(); scanner.Residues = forensics.Residues.ToList(); } + if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable")) + { + scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value); + } else + { + scanner.SolutionDNAs = new(); + } + scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName; } @@ -206,10 +220,17 @@ namespace Content.Server.Forensics } text.AppendLine(); text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas")); - foreach (var dna in component.DNAs) + foreach (var dna in component.TouchDNAs) { text.AppendLine(dna); } + foreach (var dna in component.SolutionDNAs) + { + Log.Debug(dna); + if (component.TouchDNAs.Contains(dna)) + continue; + text.AppendLine(dna); + } text.AppendLine(); text.AppendLine(Loc.GetString("forensic-scanner-interface-residues")); foreach (var residue in component.Residues) @@ -232,7 +253,8 @@ namespace Content.Server.Forensics { component.Fingerprints = new(); component.Fibers = new(); - component.DNAs = new(); + component.TouchDNAs = new(); + component.SolutionDNAs = new(); component.LastScannedName = string.Empty; UpdateUserInterface(uid, component); diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index c0d990aa59..ec311d136a 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -1,11 +1,16 @@ using Content.Server.Body.Components; +using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.DoAfter; using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics.Components; using Content.Server.Popups; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Popups; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.DoAfter; +using Content.Shared.Fluids.Components; using Content.Shared.Forensics; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; @@ -23,20 +28,35 @@ namespace Content.Server.Forensics [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + public override void Initialize() { SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnFingerprintInit); SubscribeLocalEvent(OnDNAInit); - SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnBeingGibbed); SubscribeLocalEvent(OnMeleeHit); SubscribeLocalEvent(OnRehydrated); SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) }); SubscribeLocalEvent(OnCleanForensicsDoAfter); SubscribeLocalEvent(OnTransferDnaEvent); + SubscribeLocalEvent(OnSolutionChanged); SubscribeLocalEvent>(OnUtilityVerb); + } + private void OnSolutionChanged(Entity ent, ref SolutionContainerChangedEvent ev) + { + var soln = GetSolutionsDNA(ev.Solution); + if (soln.Count > 0) + { + var comp = EnsureComp(ent.Owner); + foreach (string dna in soln) + { + comp.DNAs.Add(dna); + } + } } private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args) @@ -51,15 +71,26 @@ namespace Content.Server.Forensics private void OnDNAInit(EntityUid uid, DnaComponent component, MapInitEvent args) { - component.DNA = GenerateDNA(); + if (component.DNA == String.Empty) + { + component.DNA = GenerateDNA(); + + var ev = new GenerateDnaEvent { Owner = uid, DNA = component.DNA }; + RaiseLocalEvent(uid, ref ev); + } } - private void OnBeingGibbed(EntityUid uid, DnaComponent component, BeingGibbedEvent args) + private void OnBeingGibbed(EntityUid uid, ForensicsComponent component, BeingGibbedEvent args) { + string dna = Loc.GetString("forensics-dna-unknown"); + + if (TryComp(uid, out DnaComponent? dnaComp)) + dna = dnaComp.DNA; + foreach (EntityUid part in args.GibbedParts) { var partComp = EnsureComp(part); - partComp.DNAs.Add(component.DNA); + partComp.DNAs.Add(dna); partComp.CanDnaBeCleaned = false; } } @@ -106,6 +137,34 @@ namespace Content.Server.Forensics } } + public List GetSolutionsDNA(EntityUid uid) + { + List list = new(); + if (TryComp(uid, out var comp)) + { + foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, comp))) + { + list.AddRange(GetSolutionsDNA(soln.Comp.Solution)); + } + } + return list; + } + + public List GetSolutionsDNA(Solution soln) + { + List list = new(); + foreach (var reagent in soln.Contents) + { + foreach (var data in reagent.Reagent.EnsureReagentData()) + { + if (data is DnaData) + { + list.Add(((DnaData) data).DNA); + } + } + } + return list; + } private void OnAfterInteract(Entity cleanForensicsEntity, ref AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target == null) diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 88c5fb9459..7c69ec8ea5 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,10 +1,11 @@ -using Content.Server.Cuffs; +using Content.Server.Cuffs; using Content.Server.Forensics; using Content.Server.Humanoid; using Content.Server.Implants.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Shared.Cuffs.Components; +using Content.Shared.Forensics; using Content.Shared.Humanoid; using Content.Shared.Implants; using Content.Shared.Implants.Components; @@ -212,6 +213,9 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem if (TryComp(ent, out var dna)) { dna.DNA = _forensicsSystem.GenerateDNA(); + + var ev = new GenerateDnaEvent { Owner = ent, DNA = dna.DNA }; + RaiseLocalEvent(ent, ref ev); } if (TryComp(ent, out var fingerprint)) { diff --git a/Content.Server/Medical/VomitSystem.cs b/Content.Server/Medical/VomitSystem.cs index ec04a27db6..4cc4e538ce 100644 --- a/Content.Server/Medical/VomitSystem.cs +++ b/Content.Server/Medical/VomitSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Forensics; using Content.Server.Popups; using Content.Server.Stunnable; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.IdentityManagement; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; @@ -28,6 +29,7 @@ namespace Content.Server.Medical [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly ThirstSystem _thirst = default!; [Dependency] private readonly ForensicsSystem _forensics = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; /// /// Make an entity vomit, if they have a stomach. @@ -83,7 +85,7 @@ namespace Content.Server.Medical } // Makes a vomit solution the size of 90% of the chemicals removed from the chemstream - solution.AddReagent("Vomit", vomitAmount); // TODO: Dehardcode vomit prototype + solution.AddReagent(new ReagentId("Vomit", _bloodstream.GetEntityBloodData(uid)), vomitAmount); // TODO: Dehardcode vomit prototype } if (_puddle.TrySpillAt(uid, solution, out var puddle, false)) diff --git a/Content.Shared/Chemistry/Components/Solution.cs b/Content.Shared/Chemistry/Components/Solution.cs index 4de3c369f7..725a685a8b 100644 --- a/Content.Shared/Chemistry/Components/Solution.cs +++ b/Content.Shared/Chemistry/Components/Solution.cs @@ -147,7 +147,7 @@ namespace Content.Shared.Chemistry.Components /// /// The prototype ID of the reagent to add. /// The quantity in milli-units. - public Solution(string prototype, FixedPoint2 quantity, ReagentData? data = null) : this() + public Solution(string prototype, FixedPoint2 quantity, List? data = null) : this() { AddReagent(new ReagentId(prototype, data), quantity); } @@ -243,7 +243,7 @@ namespace Content.Shared.Chemistry.Components return false; } - public bool ContainsReagent(string reagentId, ReagentData? data) + public bool ContainsReagent(string reagentId, List? data) => ContainsReagent(new(reagentId, data)); public bool TryGetReagent(ReagentId id, out ReagentQuantity quantity) @@ -404,7 +404,7 @@ namespace Content.Shared.Chemistry.Components /// /// The prototype of the reagent to add. /// The quantity in milli-units. - public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan, ReagentData? data = null) + public void AddReagent(ReagentPrototype proto, FixedPoint2 quantity, float temperature, IPrototypeManager? protoMan, List? data = null) { if (_heatCapacityDirty) UpdateHeatCapacity(protoMan); @@ -489,7 +489,7 @@ namespace Content.Shared.Chemistry.Components { var (reagent, curQuantity) = Contents[i]; - if(reagent != toRemove.Reagent) + if(reagent.Prototype != toRemove.Reagent.Prototype) continue; var newQuantity = curQuantity - toRemove.Quantity; @@ -523,7 +523,7 @@ namespace Content.Shared.Chemistry.Components /// The prototype of the reagent to be removed. /// The amount of reagent to remove. /// How much reagent was actually removed. Zero if the reagent is not present on the solution. - public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, ReagentData? data = null) + public FixedPoint2 RemoveReagent(string prototype, FixedPoint2 quantity, List? data = null) { return RemoveReagent(new ReagentQuantity(prototype, quantity, data)); } diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index 7e00157b6e..ad9cd01fbf 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -452,7 +452,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// The amount of reagent to add. /// If all the reagent could be added. [PublicAPI] - public bool TryAddReagent(Entity soln, string prototype, FixedPoint2 quantity, float? temperature = null, ReagentData? data = null) + public bool TryAddReagent(Entity soln, string prototype, FixedPoint2 quantity, float? temperature = null, List? data = null) => TryAddReagent(soln, new ReagentQuantity(prototype, quantity, data), out _, temperature); /// @@ -464,7 +464,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// The amount of reagent to add. /// The amount of reagent successfully added. /// If all the reagent could be added. - public bool TryAddReagent(Entity soln, string prototype, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null) + public bool TryAddReagent(Entity soln, string prototype, FixedPoint2 quantity, out FixedPoint2 acceptedQuantity, float? temperature = null, List? data = null) { var reagent = new ReagentQuantity(prototype, quantity, data); return TryAddReagent(soln, reagent, out acceptedQuantity, temperature); @@ -513,7 +513,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// The Id of the reagent to remove. /// The amount of reagent to remove. /// If the reagent to remove was found in the container. - public bool RemoveReagent(Entity soln, string prototype, FixedPoint2 quantity, ReagentData? data = null) + public bool RemoveReagent(Entity soln, string prototype, FixedPoint2 quantity, List? data = null) { return RemoveReagent(soln, new ReagentQuantity(prototype, quantity, data)); } diff --git a/Content.Shared/Chemistry/Reaction/ITileReaction.cs b/Content.Shared/Chemistry/Reaction/ITileReaction.cs index af6cc34f48..31b7c9129c 100644 --- a/Content.Shared/Chemistry/Reaction/ITileReaction.cs +++ b/Content.Shared/Chemistry/Reaction/ITileReaction.cs @@ -1,4 +1,4 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Robust.Shared.Map; @@ -9,6 +9,7 @@ namespace Content.Shared.Chemistry.Reaction FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume, - IEntityManager entityManager); + IEntityManager entityManager, + List? data = null); } } diff --git a/Content.Shared/Chemistry/Reagent/DNAData.cs b/Content.Shared/Chemistry/Reagent/DNAData.cs new file mode 100644 index 0000000000..e75e994ece --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/DNAData.cs @@ -0,0 +1,28 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chemistry.Reagent; + +[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable] +public sealed partial class DnaData : ReagentData +{ + [DataField] + public string DNA = String.Empty; + + public override ReagentData Clone() => this; + + public override bool Equals(ReagentData? other) + { + if (other == null) + { + return false; + } + + return ((DnaData) other).DNA == DNA; + } + + public override int GetHashCode() + { + return DNA.GetHashCode(); + } +} diff --git a/Content.Shared/Chemistry/Reagent/ReagentId.cs b/Content.Shared/Chemistry/Reagent/ReagentId.cs index 07a4200219..798dd28db4 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentId.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentId.cs @@ -1,6 +1,7 @@ -using Content.Shared.FixedPoint; +using Content.Shared.FixedPoint; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using System.Linq; namespace Content.Shared.Chemistry.Reagent; @@ -20,17 +21,23 @@ public partial struct ReagentId : IEquatable /// Any additional data that is unique to this reagent type. E.g., for blood this could be DNA data. /// [DataField("data")] - public ReagentData? Data { get; private set; } + public List? Data { get; private set; } = new(); - public ReagentId(string prototype, ReagentData? data) + public ReagentId(string prototype, List? data) { Prototype = prototype; - Data = data; + Data = data ?? new(); } public ReagentId() { Prototype = default!; + Data = new(); + } + + public List EnsureReagentData() + { + return (Data != null) ? Data : new List(); } public bool Equals(ReagentId other) @@ -44,13 +51,13 @@ public partial struct ReagentId : IEquatable if (other.Data == null) return false; - if (Data.GetType() != other.Data.GetType()) + if (Data.Except(other.Data).Any() || other.Data.Except(Data).Any() || Data.Count != other.Data.Count) return false; - return Data.Equals(other.Data); + return true; } - public bool Equals(string prototype, ReagentData? otherData = null) + public bool Equals(string prototype, List? otherData = null) { if (Prototype != prototype) return false; @@ -73,12 +80,12 @@ public partial struct ReagentId : IEquatable public string ToString(FixedPoint2 quantity) { - return Data?.ToString(Prototype, quantity) ?? $"{Prototype}:{quantity}"; + return $"{Prototype}:{quantity}"; } public override string ToString() { - return Data?.ToString(Prototype) ?? Prototype; + return $"{Prototype}"; } public static bool operator ==(ReagentId left, ReagentId right) diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index fe937b9de4..dba2ba03a3 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -143,7 +143,7 @@ namespace Content.Shared.Chemistry.Reagent [DataField] public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6)); - public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager) + public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager, List? data) { var removed = FixedPoint2.Zero; @@ -152,7 +152,7 @@ namespace Content.Shared.Chemistry.Reagent foreach (var reaction in TileReactions) { - removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager); + removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager, data); if (removed > reactVolume) throw new Exception("Removed more than we have!"); diff --git a/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs b/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs index 9644f919f7..4cc87f5bd7 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentQuantity.cs @@ -17,7 +17,7 @@ public partial struct ReagentQuantity : IEquatable [ViewVariables] public ReagentId Reagent { get; private set; } - public ReagentQuantity(string reagentId, FixedPoint2 quantity, ReagentData? data) + public ReagentQuantity(string reagentId, FixedPoint2 quantity, List? data = null) : this(new ReagentId(reagentId, data), quantity) { } @@ -37,7 +37,7 @@ public partial struct ReagentQuantity : IEquatable return Reagent.ToString(Quantity); } - public void Deconstruct(out string prototype, out FixedPoint2 quantity, out ReagentData? data) + public void Deconstruct(out string prototype, out FixedPoint2 quantity, out List? data) { prototype = Reagent.Prototype; quantity = Quantity; diff --git a/Content.Shared/Forensics/Events.cs b/Content.Shared/Forensics/Events.cs index 7300b78d76..f7b9475cb5 100644 --- a/Content.Shared/Forensics/Events.cs +++ b/Content.Shared/Forensics/Events.cs @@ -51,3 +51,20 @@ public record struct TransferDnaEvent() /// public bool CanDnaBeCleaned = true; } + +/// +/// An event to generate and act upon new DNA for an entity. +/// +[ByRefEvent] +public record struct GenerateDnaEvent() +{ + /// + /// The entity getting new DNA. + /// + public EntityUid Owner; + + /// + /// The generated DNA. + /// + public string DNA; +} diff --git a/Content.Shared/Forensics/ForensicScannerEvent.cs b/Content.Shared/Forensics/ForensicScannerEvent.cs index ce84b1f7b3..f275680b05 100644 --- a/Content.Shared/Forensics/ForensicScannerEvent.cs +++ b/Content.Shared/Forensics/ForensicScannerEvent.cs @@ -7,7 +7,8 @@ namespace Content.Shared.Forensics { public readonly List Fingerprints = new(); public readonly List Fibers = new(); - public readonly List DNAs = new(); + public readonly List TouchDNAs = new(); + public readonly List SolutionDNAs = new(); public readonly List Residues = new(); public readonly string LastScannedName = string.Empty; public readonly TimeSpan PrintCooldown = TimeSpan.Zero; @@ -16,7 +17,8 @@ namespace Content.Shared.Forensics public ForensicScannerBoundUserInterfaceState( List fingerprints, List fibers, - List dnas, + List touchDnas, + List solutionDnas, List residues, string lastScannedName, TimeSpan printCooldown, @@ -24,7 +26,8 @@ namespace Content.Shared.Forensics { Fingerprints = fingerprints; Fibers = fibers; - DNAs = dnas; + TouchDNAs = touchDnas; + SolutionDNAs = solutionDnas; Residues = residues; LastScannedName = lastScannedName; PrintCooldown = printCooldown; diff --git a/Resources/Locale/en-US/forensics/forensics.ftl b/Resources/Locale/en-US/forensics/forensics.ftl index 29e2db8f78..712e8511bb 100644 --- a/Resources/Locale/en-US/forensics/forensics.ftl +++ b/Resources/Locale/en-US/forensics/forensics.ftl @@ -23,8 +23,10 @@ forensic-scanner-verb-message = Perform a forensic scan forensic-pad-fingerprint-name = {$entity}'s fingerprints forensic-pad-gloves-name = fibers from {$entity} +forensics-dna-unknown = unknown DNA + forensics-verb-text = Remove evidence forensics-verb-message = Remove fingerprints and DNA residues from the object! forensics-cleaning = You begin cleaning the evidence off of {THE($target)}... -forensics-cleaning-cannot-clean = There is nothing cleanable on {THE($target)}! \ No newline at end of file +forensics-cleaning-cannot-clean = There is nothing cleanable on {THE($target)}! diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 62bb923a61..36f0faa1df 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -162,3 +162,6 @@ solution: puddle - type: BadDrink - type: IgnoresFingerprints + - type: Tag + tags: + - DNASolutionScannable diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml index 0c069f615e..9dd3cffbe6 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks-cartons.yml @@ -42,6 +42,7 @@ #In future maybe add generic plastic scrap trash/debris - type: TrashOnSolutionEmpty solution: drink + - type: DnaSubstanceTrace - type: entity parent: DrinkCartonBaseFull diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index c435e6c98f..4a9d5c0463 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -35,6 +35,7 @@ interfaces: enum.TransferAmountUiKey.Key: type: TransferAmountBoundUserInterface + - type: DnaSubstanceTrace - type: entity parent: DrinkBase diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index a5f72b8546..8630912451 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -39,6 +39,7 @@ - type: PhysicalComposition materialComposition: Plastic: 100 + - type: DnaSubstanceTrace - type: PressurizedSolution solution: drink - type: Shakeable diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index b6488dbe8b..f5b733d37b 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -66,6 +66,7 @@ - type: Tag tags: - DrinkCan + - type: DnaSubstanceTrace - type: entity parent: DrinkCanBaseFull diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml index d13c9ad2ca..f0103cc219 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cups.yml @@ -38,6 +38,7 @@ damage: types: Blunt: 0 + - type: DnaSubstanceTrace - type: entity parent: DrinkBaseCup diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml index dfb6ac31c0..930cf81757 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml @@ -34,6 +34,7 @@ - type: PhysicalComposition materialComposition: Steel: 50 + - type: DnaSubstanceTrace - type: ReactionMixer mixOnInteract: false reactionTypes: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml index 9f94be9576..eadeeabd74 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/trash_drinks.yml @@ -76,6 +76,7 @@ materialComposition: Glass: 100 - type: SpaceGarbage + - type: DnaSubstanceTrace - type: entity name: base empty bottle diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 2a6f314904..ccb7900810 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -50,6 +50,7 @@ - type: GuideHelp guides: - Janitorial + - type: DnaSubstanceTrace - type: entity parent: BaseItem @@ -109,6 +110,7 @@ tags: - Mop - MopAdv + - type: DnaSubstanceTrace - type: entity name: wet floor sign @@ -332,3 +334,4 @@ - type: CleansForensics - type: Fiber fiberColor: fibers-white + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml index fd931a05c7..d1020ff609 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml @@ -82,6 +82,7 @@ - type: Tag tags: - ChemDispensable + - type: DnaSubstanceTrace - type: entity parent: Jug diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml index 6fdb77fe5f..f8f55cf8fc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-bottles.yml @@ -93,6 +93,7 @@ transferForensics: true - !type:DoActsBehavior acts: [ "Destruction" ] + - type: DnaSubstanceTrace - type: entity parent: BaseChemistryEmptyBottle diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml index 39a35dba8c..eca666ca4f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry-vials.yml @@ -101,6 +101,7 @@ acts: [ "Destruction" ] - type: Spillable solution: beaker + - type: DnaSubstanceTrace - type: entity id: VestineChemistryVial diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index c888559bc3..72f612436b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -92,6 +92,7 @@ Blunt: 5 - type: StaticPrice price: 30 + - type: DnaSubstanceTrace - type: entity parent: BaseItem @@ -153,6 +154,7 @@ damageModifierSet: Glass - type: StaticPrice price: 30 + - type: DnaSubstanceTrace - type: entity name: beaker @@ -303,6 +305,7 @@ inHandsFillBaseName: -fill- - type: StaticPrice price: 40 + - type: DnaSubstanceTrace - type: Tag tags: - Dropper diff --git a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml index 58c8dae2b0..b64aa3a041 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/bucket.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/bucket.yml @@ -67,3 +67,4 @@ - type: PhysicalComposition materialComposition: Plastic: 50 + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml index 9fd05beaee..6ed06addcd 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml @@ -84,6 +84,7 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] + - type: DnaSubstanceTrace - type: Fixtures fixtures: fix1: @@ -334,3 +335,4 @@ - type: GuideHelp guides: - Janitorial + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 197966b491..adf25cc71d 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -509,6 +509,9 @@ - type: Tag id: DiscreteHealthAnalyzer #So construction recipes don't eat medical PDAs +- type: Tag + id: DNASolutionScannable + - type: Tag id: DockArrivals